AOP原理分析(三)从Spring的AOP说开去

前面两篇文章我们讲解了整个AOP切面的实现,刚开始我以为AOP也就这么多东西了,而当我总结起来时才发现,这仅仅是AOP的开始,相信你看完本篇文章会有:曲径通幽,豁然开朗 的感觉

从AOP到方法论

我们还是来看看AOP为我们搭建了一个怎样的框架(或者说一个黑盒吧):整个链路的流程如下:

准备阶段:查找所有增强器--> 筛选可以应用到当前对象的增强器 --> 构建代理对象

执行阶段:当代理对象的方法被调用时 --> 构建责任链 --> 执行时走责任链逻辑;

其中查找所有的增强器:Spring会获取所有的Advisor,如果开启了@Aspect的功能就会额外加载标有@Aspect的类,然后将标有@Before、@After等注解的方法封装成Advisor【InstantiationModelAwarePointcutAdvisorImpl】;
筛选可以应用到当前对象的增强器:主要分类两类的筛选:一类是IntroductionAdvisor即类级别的切入,这一类是通过Advisor中的ClassFilter来进行判断是否切入当前对象;另一个是PointcutAdvisor即方法级别的切入,会通过PointCut中的ClassFilter和MethodMatcher分别来进行类及方法级别的筛选,判断是都切入当前方法。构建代理对象,以及执行时责任链的封装这些都不需要我们来管的。总的来说,我们如果需要使用Sping的这套AOP框架来实现我们的业务逻辑,我们只需要写好自己的增强器(Advisor)就可以了,而这个过程中就包含了两个主要的部分:1、编写Advice,也就是我们的拦截逻辑;2、拦截的筛选规则,也就是上面提到的ClassFilter或者PointCut;也就是告诉Spring你要拦截什么,怎么拦截。
好了,说到这里我相信很多人应该都明白如何通过Spring提供的AOP框架来实现自己的业务逻辑拦截,如果没懂也没关系,接下来我们看看Spring家自己是如何在这个AOP框架的基础上扩展出更多功能的:

AOP方法论在Spring中的身影

1、Spring的事务实现

要看Spring的事务是如何实现的,我们看看开启Spring事务时都引入了什么?那我们就要从@EnableTransactionManagement注解到入了什么组件到容器中下手,let we see see :
@EnableTransactionManagement导入了TransactionManagementConfigurationSelector类,TransactionManagementConfigurationSelector帮我们导入了AutoProxyRegistrar和ProxyTransactionManagementConfiguration这两个类,前者是为了能开启AOP的功能,我们先不说,ProxyTransactionManagementConfiguration这个类才是Spring事务发挥作用的关键先生,一起来一探究竟吧:

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

    // 创建了用于事务的Advisor
    @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
            TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {

        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        // 设置transactionAttributeSource
        advisor.setTransactionAttributeSource(transactionAttributeSource);
        // 设置Advice
        advisor.setAdvice(transactionInterceptor);
        if (this.enableTx != null) {
            advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
        }
        return advisor;
    }

    //封装事务管理配置的参数,@Transactional(..)中的参数
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionAttributeSource transactionAttributeSource() {
        return new AnnotationTransactionAttributeSource();
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
        TransactionInterceptor interceptor = new TransactionInterceptor();
        interceptor.setTransactionAttributeSource(transactionAttributeSource);
        if (this.txManager != null) {
            interceptor.setTransactionManager(this.txManager);
        }
        return interceptor;
    }

}

哦豁,映入眼帘的是熟悉的Advisor和Advice,也是验证我们上面所说的方法论。就从我们熟练使用Spring申明是事务注解@Transactional的角度来说,PointCut中肯定就会匹配@Transactional这个注解,然后在Advice中帮我们建立连接,开启事务,执行sql,提交或回滚事务。详细的事务讲解,我会留在下一个模块中来讲解,毕竟Spring事务中还是有很多有趣的点的,而且他也是面试的常客之一啊,不单独给一个模块描述不应该。

2、Spring的缓存实现

在查看了Spring事务的逻辑,再来看Spring的缓存逻辑其实是大差不差的,我们直接来看开启Spring缓存的@EnableCaching注解为我们向Spring中导入了什么组件,其实在不引入JSR107和JcacheImpl的情况下,也是为我们引入了两个组件:AutoProxyRegistrar和ProxyCachingConfiguration,同样,前者我们先不关心,我们来看看ProxyCachingConfiguration里给我们带来了什么:

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {

    @Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor(
            CacheOperationSource cacheOperationSource, CacheInterceptor cacheInterceptor) {

        BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
        advisor.setCacheOperationSource(cacheOperationSource);
        advisor.setAdvice(cacheInterceptor);
        if (this.enableCaching != null) {
            advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
        }
        return advisor;
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public CacheOperationSource cacheOperationSource() {
        return new AnnotationCacheOperationSource();
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public CacheInterceptor cacheInterceptor(CacheOperationSource cacheOperationSource) {
        CacheInterceptor interceptor = new CacheInterceptor();
        interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
        interceptor.setCacheOperationSource(cacheOperationSource);
        return interceptor;
    }

}

我相信,你看到这一串代码,你都不敢相信吧,这和事务的逻辑几乎一模一样啊,是不是,CacheAdvisor以及CacheInterceptor,都是同样的套路,都是上面所说的方法论,用起来简直不要太方便。

3、Spring异步的实现

看了上面Spring事务和Spring缓存的实现,对于Spring异步De实现你是不是也能猜到是怎么实现的了,我们直接看@EnableAsync引入的ProxyAsyncConfiguration吧:

    public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {

    @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
        Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
        AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
        bpp.configure(this.executor, this.exceptionHandler);
        Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
        if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
            bpp.setAsyncAnnotationType(customAsyncAnnotation);
        }
        bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
        bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
        return bpp;
    }

}

乍看第一眼,我们并没有看到我们预料中的Advisor和Interceptor(Advice),而是构建了一个Bean的后置处理器AsyncAnnotationBeanPostProcessor,并为这个后置处理器配置了线程池executor和异常处理器exceptionHandler,那么异步的处理就是在AsyncAnnotationBeanPostProcessor中实现的,我们在进一步看看AsyncAnnotationBeanPostProcessor这个类,它里面的实现究竟是怎么样的呢?

    public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {


    public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME =
            AnnotationAsyncExecutionInterceptor.DEFAULT_TASK_EXECUTOR_BEAN_NAME;


    protected final Log logger = LogFactory.getLog(getClass());

    // 此处省略若干行代码

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        super.setBeanFactory(beanFactory);

        // 此处注入了 AsyncAnnotationAdvisor
        AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
        if (this.asyncAnnotationType != null) {
            advisor.setAsyncAnnotationType(this.asyncAnnotationType);
        }
        advisor.setBeanFactory(beanFactory);
        this.advisor = advisor;
    }

}

其中的一起配置方法我们不看,我们就看下最后的setBeanFactory方法,哈哈,惊不惊喜,意不意外,一个大大的Advisor映入了眼帘,这不就是我们期望的吗,读者可能会问那Adice在哪呢?难道你忘记了Advisor是Advice和Pointcut的整合,那么Advisor中当然包含了Advice和Poincut的构建了,不妨一看:

        public AsyncAnnotationAdvisor(
            @Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {

        Set<Class<? extends Annotation>> asyncAnnotationTypes = new LinkedHashSet<>(2);
        asyncAnnotationTypes.add(Async.class);
        try {
            asyncAnnotationTypes.add((Class<? extends Annotation>)
                    ClassUtils.forName("javax.ejb.Asynchronous", AsyncAnnotationAdvisor.class.getClassLoader()));
        }
        catch (ClassNotFoundException ex) {
            // If EJB 3.1 API not present, simply ignore.
        }
        this.advice = buildAdvice(executor, exceptionHandler);
        this.pointcut = buildPointcut(asyncAnnotationTypes);
    }

buildAdvice,buildPointcut这两个方法就是分别来构建Advice和Pointcut的。
但是Spring异步的实现和前面两个的实现确实是有点不同的,首先我们发现在注入的时候@EnableAsync仅仅引入了一个ProxyAsyncConfiguration,并没有像前面两个一样引入AutoProxyRegistrar,而AutoProxyRegistrar的作用就是基于自己是BeanPostProcessor通过bean的生命周期方法postProcessBeforeInstantiation来帮助完成扫描缓存所有的Advisor,然后再通过bean的生命周期方法postProcessAfterInstantiation帮忙给可以应用Advisor的bean创建代理对象,而能够在执行的时候走到责任链的拦截模式关键就是创建出代理对象;那么Spring异步是如何来帮助我们创建代理对象的呢?其实你被忘了,Spring异步的实现本来就是靠的一个BeanPostProcesor即AsyncAnnotationBeanPostProcessor,所以Spring家将创建代理对象的逻辑也就放在了AsyncAnnotationBeanPostProcessor的postProcessAfterInitialization方法中。看到这个地方,你有没有一个疑问,如果我同时在一个方法上标注了@Transactional和@Async注解,那么它不是会走两遍创建代理对象的逻辑吗?确实是这样的,Spring也对这一情况做了兼容,来一起看下AsyncAnnotationBeanPostProcessor的postProcessAfterInitialization这个方法:

    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (this.advisor == null || bean instanceof AopInfrastructureBean) {
            // Ignore AOP infrastructure such as scoped proxies.
            return bean;
        }

        // 兼容走aop的逻辑已经生成了的代理对象,那么只需要将异步的这个增强器添加到代理对象的增强器集合中就可以了
        if (bean instanceof Advised) {
            Advised advised = (Advised) bean;
            if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
                // Add our local Advisor to the existing proxy's Advisor chain...
                if (this.beforeExistingAdvisors) {
                    advised.addAdvisor(0, this.advisor);
                }
                else {
                    advised.addAdvisor(this.advisor);
                }
                return bean;
            }
        }
        // 帮助创建代理对象
        if (isEligible(bean, beanName)) {
            ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
            if (!proxyFactory.isProxyTargetClass()) {
                evaluateProxyInterfaces(bean.getClass(), proxyFactory);
            }
            proxyFactory.addAdvisor(this.advisor);
            customizeProxyFactory(proxyFactory);
            return proxyFactory.getProxy(getProxyClassLoader());
        }

        // No proxy needed.
        return bean;
    }

如果当前这个bean已经经历过了AOP的增强成代理对象,那么它就属于Advised类型的,我们只需要把当前这个异步的Advisor加入到这个代理对象的增强器链中就可以了;而如果这个bean对象没有经历过AOP的代理,那么就会在此处为它创建代理对象,然后的执行流程都是一样的。至于为什么Spring在实现异步封装的时候没有像实现事务和缓存那样直接基于AOP框架来实现,我也不是很清楚,可能是为了提供一种新的方式来实现告诉读者是可以一题多解的吧,也有可能就是不同的人有不同的想法呢,毕竟作者也不是同一个人,而且这种情况在Spring中也不是第一次出现了,前面我们讲的增强器转拦截器一样使用两种不同的实现方法。

写在最后

好了,说到这里这篇文章也就差不多了,虽然我只讲了Spring中事务,缓存,异步这三种实现的应用,但是在Spring中基于AOP框架的实现不仅仅于此,还有@Validated以及mybatis-plus中多数据源@DS的实现,等等等等。我们需要的是掌握这个方法论,懂了方法论,不管是在以后的开发中,还是以后看别的源码时,就能很快的了解实现的原理,是事倍功倍的。关于AOP就讲到这里了;接下来,我会开始讲解Spring的事务实现,这也是面试的一个高频点,希望对有缘人有所帮助。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容