欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringAOP源码分析

程序员文章站 2022-07-05 16:45:56
目录写在前面写在前面今天我们主要分析springAOP的源码流程,因为博主本人习惯于使用AspectJ和注解的方式使用AOP,因此本次的分析是基于@EnableAspectJAutoProxy注解和cglib动态代理的AOP,...

写在前面

本文简介:今天我们主要分析springAOP的源码流程,因为博主本人习惯于使用AspectJ和注解的方式使用AOP,因此本次的分析是基于@EnableAspectJAutoProxy注解和cglib动态代理的AOP,其实JDK的动态代理基本类似,本文也会有所涉及,温馨提醒,源码分析流程很长,慎入,需要看大概流程的小伙伴直接看总结部分即可。

如何分析SpringAOP源码?

其实我们要分析spring某些功能的源码只需要抓住以下三个点。
1.我们使用这个功能时,往IOC容器中添加了什么东西,这个东西是什么?
2.我们往IOC容器添加的东西是什么时候执行的?
3.我们往IOC容器中添加的东西是怎么进行执行的,执行的结果是什么?
我们后续的源码分析将会基于以上三个问题,希望大家带着问题进行思考。

源码分析

@EnableAspectJAutoProxy注解

我们在配置类上加上@EnableAspectJAutoProxy或者在配置文件中加上< aop:aspectj-autoproxy >< /aop:aspectj-autoproxy>注解AOP功能就生效了,我们不添加该注解,AOP将无法生效。所以该注解是我们研究AOP功能的关键,我们点进这个注解看一看。
SpringAOP源码分析
该注解使用了@Import(AspectJAutoProxyRegistrar.class),@Import注解将会帮助我们往IOC容器中自定义导入组件,可以直接传入组件的class、一个ImportSelector的子类、或者是ImportBeanDefinitionRegistrar。这里导入的AspectJAutoProxyRegistrar是ImportBeanDefinitionRegistrar的子类SpringAOP源码分析

他实现了一个registerBeanDefinitions方法,会在IOC容器创建时往IOC容器中注册组件信息。我们在该方法打上断点。debug执行
SpringAOP源码分析
代码停在了该行,我们注意下方法调用栈
SpringAOP源码分析
我们实际是在IOC容器创建的Refresh方法中调用了invokeBeanFactoryPostProcessors(beanFactory)方法一步一步执行过来的

SpringAOP源码分析
ok,知道该方法在IOC容器Refresh方法执行的位置后,我们回到我们的第一张图registerBeanDefinitions()方法
SpringAOP源码分析
我们跟进这个方法,我们来到了AopConfig的registerOrEscalateApcAsRequired()方法
SpringAOP源码分析
该放方法的主要步骤如图
SpringAOP源码分析
该方法中的常量AUTO_PROXY_CREATOR_BEAN_NAME =SpringAOP源码分析
该方法的cls为
SpringAOP源码分析
所以这一步实际上我们是往BeanDefinitionRegistry中注册了一个Bean,bean的名字为org.springframework.aop.config.internalAutoProxyCreator,bean的类型为:org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator

本步流程总结

使用AspectJAutoProxyRegistrar给我们的BeanDefinitionRegistry中注册一个bean,bean的name为:internalAutoProxyCreator内部自动代理创建器,bean的类型为AnnotationAwareAspectJAutoProxyCreator。

AnnotationAwareAspectJAutoProxyCreator

上一步我们往IOC容器中注册了AnnotationAwareAspectJAutoProxyCreator信息,那么我们注册的这个东西到底有什么用呢?我们进入这个类进行查看
SpringAOP源码分析
这是该类的继承结构,我们到AbstractAutoProxyCreator中,我们发现该类是BeanFactoryAware和SmartInstantiationAwareBeanPostProcessor接口的是实现类,SmartInstantiationAwareBeanPostProcessor是BeanPostProcessor接口的子接口,BeanFactoryAware是Aware的子接口。
BeanPostProcessor会在对象的初始化方法进行拦截操作
BeanFactoryAware属于XXXAware类接口有SetXXX方法,能往我们的组件中注入IOC容器底层的组件。
因此我们找到AnnotationAwareAspectJAutoProxyCreator类中上述方法的位置,并且打上断点。
SpringAOP源码分析
上述接口方法的分布如下,我们在对应位置打上断点Debug运行
SpringAOP源码分析
代码停在了AbstractAdvisorAutoProxyCreator的setBeanFactory()方法处
SpringAOP源码分析
首先我们调用了super的setBeanfactory中往该对象中注入了BeanFactory
SpringAOP源码分析
继续往下看,我们调用了初始化beanFacroy的方法,我们跟进查看
SpringAOP源码分析
我们将beanFactory进行封装,传给了aspectJAdvisorsBuilder。
SpringAOP源码分析
然后我们返回,发现回到了AbstractAutowireCapableBeanFactory类中,该类是我们创建Bean并将其保存在IOC容器中的核心业务Bean,里面有我们的创建bean,属性注入,后置处理器,初始化方法等所有业务逻辑。

SpringAOP源码分析
看到这里我们就明白了,我们上一步注册好我们的AnnotationAwareAspectJAutoProxyCreator之后,这一步将其加入到了容器中。这一步刚好是refresh方法中的registerBeanPostProcessors方法,刚好在上一步后面。
SpringAOP源码分析

本步流程总结

上一步我们将AnnotationAwareAspectJAutoProxyCreator注册到BeanDifinitionRegistry中后,在refresh的registerBeanPostProcessors方法中,我们将其加入了IOC容器中。

AOP业务方法执行

上一步我们将AnnotationAwareAspectJAutoProxyCreator加入到IOC容器中后,我们知道AnnotationAwareAspectJAutoProxyCreator是BeanPostProcessor的实现类,因此将会在我们Bean的初始化前后对bean进行操作,因此我们给切点类Bean的构造器上打上断点,我们查看切点类Bean是如何创建的。
第一步我们在refresh()方法中调用了finishBeanFactoryInitialization()方法,从该方法上面的注释也能看出,该方法是实例化所有剩余的Bean
SpringAOP源码分析
我们跟进这个方法,我们来到了preInstantiateSingletons()方法,
SpringAOP源码分析
该方法先获取出所有Bean的name,然后遍历name集合,选出!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()不是抽象,是单例的并且不是懒加载的bean进行获取,调用getBean方法
SpringAOP源码分析
getBean方法–>>doGetBean() -->>getSingleton()–>>createBean()
SpringAOP源码分析
然后我们停在了一个叫做resolveBeforeInstantiation()实例化前处理方法,这方法后续是真正的创建Bean的方法,但是如果当前方法返回值不为空我们就直接返回了,这说明我们希望做一些事情,看看能不呢直接返回需要的Bean而不是去创建这个Bean,我们继续跟进这个方法

SpringAOP源码分析
我们来到了postProcessBeforeInstantiation()方法,这个方法是属于SmartInstantiationAwareBeanPostProcessor接口的,该方法会在对象创建前进行处理,在这里尝试创建代理对象进行返回,但是我们并不满足当前条件,改方法直接返回null
SpringAOP源码分析

在创建bean之前我们没有实现返回代理对象的操作,我们的代码停在了创建对象之后的postProcessAfterInitialization方法,改方法调用了wrapIfNecessary()包装对象如果需要的话;方法,我们跟进去看看
SpringAOP源码分析
该方法先判断当前bean是否为空啊、是否已经进行过增强、是否需要跳过等判断。
SpringAOP源码分析
最终我们停在了这一步,我们跟进去看看SpringAOP源码分析
我们发现实际上调用了aspectJAdvisorsBuilder.buildAspectJAdvisors()去创建我们的增强器将其返回
SpringAOP源码分析
SpringAOP源码分析

然后我们advisedBeans已经增强bean中标准当前bean的增强情况为True,调用了createProxy()将我们bean,方法,参数,增强器等内容传入了进去。

SpringAOP源码分析

我们进入到createProxy方法,发现他先创建了一个ProxyFactory,然后将我们的this对象封装进去了,然后将我们的specificInterceptors解析为Advisor[] advisors,实际上就是对我们的Interceptor进行包装this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
SpringAOP源码分析
SpringAOP源码分析
最终调用了proxyFactory.getProxy(getProxyClassLoader());,我们继续跟进
SpringAOP源码分析
调用了createAopProxy()方法,我们继续进入getAopProxyFactory().createAopProxy(this);方法
SpringAOP源码分析

终于我们看到了我们代理对象的创建代码,spring会帮我们进行判断如果是实现了接口的情况会生成jdk代理,否则给我们生成Cglib动态代理对象。
SpringAOP源码分析
最终我们将我们生成的cglib代理对象进行返回。

本步总结

我们在创建切点类的时候,会通过BeanPostProcessor的postProcessAfterInitialization的将我们的bean对象封装为代理对象,如果我们使用的是实现接口的方式,那么生成的是jdk的动态代理。如果我们使用的是aspectJ的方式,那么生成的将是Cglib的动态代理。

代理对象的执行过程

上一步我们生成了我们的动态代理对象,然后我们需要看看我们代理对象是如何执行切点方法的,我们在切点方法上加上一个断点。
我们的方法停在了 CglibAopProxy 的getInterceptorsAndDynamicInterceptionAdvice()方法,他返回了一个集合对象,我们跟进这个方法
SpringAOP源码分析
我们发现他先想从缓存中获取cached,如果从缓存中获取不到,那么将调用getInterceptorsAndDynamicInterceptionAdvice方法,我们跟进这个方法

SpringAOP源码分析
该方法内部先创建了一个和我们的增强器数组一样大小的集合,然后遍历我们的增强器
SpringAOP源码分析

该方法内部的核心代码就是如下两个将 advisor 都转换为MethodInterceptor类型,然后将其加入集合中进行返回。
SpringAOP源码分析

SpringAOP源码分析
实际上将advisor转为MethodInterceptor对象就是判断如果直接是MethodInterceptor的类型,那么加入集合,否则使用AdvisorAdapter增强适配器将其包装为MethodInterceptor再加入集合。
那么我们的MethodInterceptor链就创建好了,实际上是将我们的增强器封装成方法拦截链而已。
SpringAOP源码分析
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
这是我们的业务执行的核心方法,将我们的执行方法对象和方法信息,参数即拦截器链统统传入。我们来看看这个方法的执行。
我们当前拦截器的顺序为
SpringAOP源码分析

然后proceed方法的执行过程如下
SpringAOP源码分析
SpringAOP源码分析
currentInterceptorIndex的下标代表当前已经执行完到哪个拦截器了,默认为-1,然后如果currentInterceptorIndex变为拦截器的的集合的size - 1时将会调用invokeJoinpoint()即切点对象的方法,我们分析一下执行流程。
SpringAOP源码分析
第一次currentInterceptorIndex自增为0,执行ExposeInvocationInterceptor的invoke方法,进入invoke方法
SpringAOP源码分析
我们发现调回了proceed方法。
第二次执行proceed方法:currentInterceptorIndex自增为1,执行AspectJAfterThrowingAdvice的invoke方法,进入invoke方法(不知道你是否有疑惑,为什么先执行的时afterThrowing方法,有疑惑就对了,我们继续往后看)
SpringAOP源码分析
进入invoke方法,我们发现又是先执行proceed方法,发生异常才执行异常增强。我们继续去proceed方法
SpringAOP源码分析
第三次执行proceed方法:currentInterceptorIndex自增为2,执行AspectJAfterReturnAdvice的invoke方法,进入invoke方法
SpringAOP源码分析
我们发现invoke方法时先执行,如果没有异常就执行返回通知,那么有异常呢?这里也没有trycatch语句啊,大家不要忘了,我们后续的方法调用,是在第二个的try块里面执行的,所以有异常将会被捕获到那里执行,也就是没有异常正常返回通知,有异常异常通知。现在大家是不是有点眉目了呢?

SpringAOP源码分析
第三次执行proceed方法:currentInterceptorIndex自增为3,执行AspectJAfterAdvice的invoke方法,进入invoke方法
SpringAOP源码分析
该方法是直接先调用proceed方法,并且方法无论如何都会执行后置增强代码。(写spring的大佬简直太牛了,我本来以为AOP是通过嵌套try catch finally的代码实现的执行顺序,没想到是这样的!)
SpringAOP源码分析
第四次执行proceed方法:currentInterceptorIndex自增为4,执行AspectJBeforerAdvice的invoke方法,进入invoke方法
SpringAOP源码分析
我们发现before的增强处理是先执行前置增强方法,再执行proceed方法
SpringAOP源码分析
第四次执行proceed方法:currentInterceptorIndex起始即为4,执行切点方法然后返回。
SpringAOP源码分析

到这里大家应该明白了为什么我们AOP的执行流程是下面的情况
正常流程:前置通知-业务方法-后置通知-返回通知
异常流程:前置通知-业务方法-后置通知-异常通知
我们逆序调用我们的增强业务,将前一个拦截链先压入栈中,待方法弹栈结果决定当前方法的执行情况(膜拜写spring的大佬)
在方法链的调用过程中实际上用到了责任链的设计模式。

本步总结

这一步我们实际上是将我们的Advisors增强器包装为MethodInterceptor拦截器,然后通过责任链的设计模式,实现对我们业务方法的增强处理。

全文总结

SpringAOP源码分析

总结
1.在BeanDefinitionRegistry中注册 AnnotationAwareAspectJAutoProxyCreator
在Refresh()方法的dnvokeBeanFactoryPostProcessors()方法中通过@EnableAspectJAutoProxy
注解我们往IOC容器中注册了 AnnotationAwareAspectJAutoProxyCreator 的信息
2.将AnnotationAwareAspectJAutoProxyCreator加入到IOC容器中
 在Refresh()方法中的 registerBeanPostProcessors()方法中,将我们的AnnotationAwareAspectJAutoProxyCreator
 加入到了IOC容器中,并且创建了aspectAdvisorBuilder对象。
3.获取代理对象
在refresh()方法中的finishBeanFactoryInitialization()方法里面我们通过AnnotationAwareAspectJAutoProxyCreator后置处理器的后置处理方法,
我们会判断组件是否需要进行增强处理,如果需要则调用了aspectJAdvisorsBuilder.buildAspectJAdvisors()
方法创建出我们的增强器,然后spring根据我们AOP的使用类型帮我们创建出JDK
或者Cglib的动态代理对象
4.执行代理方法
我们将Advisor封装为MethodInterceptors方法拦截链,逆序执行AOP的增强方法,通过责任链的设计模式完成我们的增强处理。

本文地址:https://blog.csdn.net/shangshanzixu/article/details/112883171