Spring中主要有两大核心模块,其中一个就是AOP(切面编程)。通常我们使用SpringAOP注解版的时候都是定义一个切面类,里面定义好切入点,然后对定义好的切入点可以进行@Before前置通知处理,@After后置通知处理,@AfterReturning返回通知处理,@AfterThrowing异常通知处理,@Around环绕通知处理,最后在配置类上面(或者通常我们在SpringBoot中的启动类上面)加上@EnableAspectJAutoProxy注解,这样我们在Spring中AOP的配置就配置好了。
@EnableAspectJAutoProxy注解的作用?
顾名思义,这个注解就是开启AOP切面编程的意思,在SpringBoot中使用到了很多@EnableXXX这样的注解,这些注解大多数都是用来开启某项功能的,里面其实大多数都是使用包装了@Import注解。@Import注解作用就是导入一个组件到IOC容器中的,它有三种导入的组件的方式
- 以类对象的形式导入,如@Import(Car.Class)这样就相当于导入了一个Car对象到容器中
- 自定义一个类继承自ImportSelect,重写里面的selectImports方法返回需要导入到容器的组件的全类名
- 自定义一个类继承自ImportBeanDefinitionRegistrar,重写里面的registerBeanDefinitions方法,利用参数BeanDefinitionRegistry.registerBeanDefinition(“book”,new RootBeanDefinition(Book.class))把组件导入到容器中,
那么我们点进@EnableAspectJProxy注解来看看
果然里面也是用的@Import注解,再看@Import注解里面的类
可以发现这里用的是第三种方式导入组件的,那么这里到底是导入了哪个Bean尼?我们深入registerAspectJAnnotationAutoProxyCreatorIfNecessary这个方法
所以我们到这里可以知道,导入的这个组件就是AnnotationAwareAspectJAutoProxyCreator这个类了,并且它的id是org.springframework.aop.config.internalAutoProxyCreator。
AnnotationAwareAspectJAutoProxyCreator这个类的作用?
1.注册AnnotationAwareAspectJAutoProxyCreator
首先我们要知道这个类的作用就先去这个类看下它的继承结构,看看这个类是否是继承了一些特殊的类以至于在Spring容器初始化的时候有特殊的处理。
进去到这个类,发现该类的继承层级较高,下面给出这个类的继承结构:
AnnotationAwareAspectJAutoProxyCreator
->AspectJAwareAdvisorAutoProxyCreator
->AbstractAdvisorAutoProxyCreator
->AbstractAutoProxyCreator(上面还有父类)
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
可以发现在它的父类AbstractAutoProxyCreator中还实现了两个接口。分别是和初始化Bean相关的SmartInstantiationAwareBeanPostProcessor,以及和初始化BeanFactory相关的BeanFactoryAware这两个接口。那么我们可以在AnnotationAwareAspectJAutoProxyCreator以及它的父类里面寻找下这两个接口的方法,顺便打上断点。我们可以发现AbstractAdvisorAutoProxyCreator这个类是重写了BeanFactoryAware的setBeanFactory方法的,然后里面还调用的一个initBeanFactory方法,而该方法也被AnnotationAwareAspectJAutoProxyCreator这个类所重写了,所以我们在AbstractAdvisorAutoProxyCreator的setBeanFactory上打上断点,以及AbstractAutoProxyCreator的postProcessBeforeInstantiation这个方法打上断点。然后我们可以Debug一下,看下方法都是怎么被调用的。
一启动项目我们发现程序就来到了setBeanFactory方法中了,然后我们此时可以看左下角的方法调用栈来看是怎么一步步走到setBeanFactory这个方法的。
首先一开始我们需要初始化我们的容器,所以就来到了refresh方法表示刷新容器
在refresh方法中,有一个方法是用来注册BeanPostProcessor的,其实实际上就是创建BeanPostProcessor对象保存到容器中的过程。而我们关注的AnnotationAwareAspectJAutoProxyCreator这个类也是间接实现了BeanPostProcessor接口的,那么里面的工作和我们AnnotationAwareAspectJAutoProxyCreator这个类关系就很大了。
然后一路来到PostProcessorRegistrationDelegate的registerBeanPostProcessors方法中,我们可以在这个方法打上断点,看里面做了哪些工作:
来到第一步拿到所有的BeanPostProcessor的全类名
接下来的逻辑都差不多一样,就是拿到BeanPostProcessor实例判断是优先级,由于我们的AnnotationAwareAspectJAutoProxyCreator实现的是Ordered接口,所以我们来到了Ordered的判断分支,把属于Ordered的BeanPostProcessor的全类名加入到了Ordered的List集合中,然后下面是遍历该集合初始化对应的BeanPostProcessor实例,这里具体就是指AnnotationAwareAspectJAutoProxyCreator
然后我们一直深入beanFactory.getBean这个方法,来到doCreateBean()方法,执行下去来到initializeBean方法
深入这个方法
所以这样就来到了我们打断点的地方了,我们可以接下来再看后面的初始化操作,把整个initializeBean方法都走完
最后把每一个BeanPostProcessor都加入到BeanFactory中
至此,AnnotationAwareAspectJAutoProxyCreator的注册流程大概就走完了,我们来总结一下AnnotationAwareAspectJAutoProxyCreator的注册过程:
- 获取IOC容器中已经定义好的所有BeanPostProcessor的全类名,包括我们的@EnableAspectJAutoProxy注解作用的需要导入的AnnotationAwareAspectJAutoProxyCreator组件
- 给容器中加别的BeanPostProcessor
- 先注册实现了PriorityOrdered接口的BeanPostProcessor
- 再注册实现了Ordered接口的BeanPostProcessor
- 最后注册普通的BeanPostProcessor
- 调用getBean方法根据全类名去注册(创建)BeanPostProcessor对象,保存在容器中,这里具体是指创建AnnotationAwareAspectJAutoProxyCreator这个BeanPostProcessor,在这个过程中,包括了创建Bean实例,populateBean()->给Bean各种属性赋值,处理Aware接口的回调,应用后置处理器postProcessBeforeInitialization(),执行自定义的init方法,应用后置处理器的postProcessAfterInitialization()
- 把注册好BeanPostProcessor加入到BeanFactory中->beanFactory.addBeanPostProcessor(postProcessor)
2.AnnotationAwareAspectJAutoProxyCreator的作用
上面我们说了AnnotationAwareAspectJAutoProxyCreator在容器中的注册(创建)过程,那么创建这个组件有什么作用尼?其实我们可以发现这个类其实间接也是InstantiationAwareBeanPostProcessor这个接口的实现类,而这个接口继承的是BeanPostProcessor这个接口
所以说我们创建的AnnotationAwareAspectJAutoProxyCreator是InstantiationAwareBeanPostProcessor的一个BeanPostProcessor,而实现了这个接口的BeanPostProcessor有什么特殊的作用尼?其实在Spring的refresh方法初始化容器的时候有一步是初始化剩余的Bean的方法,这个方法就是真正地初始化我们的平时自定义的类了,下面我们来看下这个方法,因为在初始化这些类的时候,实现了InstantiationAwareBeanPostProcessor这个接口的BeanPostProcessor会在里面起作用。
我们在AbstractAutoProxyCreator中重写InstantiationAwareBeanPostProcessor这个接口的postProcessBeforeInstantiation方法打上断点,看这个过程到底经过了哪些方法
第一个来到的就是refresh方法里面的finishBeanFactoryInitialization方法
一直走到getBean方法
继续走,来到下面的方法
如果resolveBeforeInstantiation这个方法返回的bean不是null就直接返回,创建成功,否则就使用doCreateBean方法去创建bean
我们深入resolveBeforeInstantiation这个方法
深入applyBeanPostProcessorsBeforeInstantiation这个方法
可以看到这里就是InstantiationAwareBeanPostProcessor接口的作用了,在创建Bean实例之前,会先拿到所有的BeanPostProcessor,如果遍历到的这个BeanPostProcessor是实现了InstantiationAwareBeanPostProcessor接口的话,那么就使用这个BeanPostProcessor调用它重写的postProcessBeforeInstantiation方法去尝试返回一个Bean实例,如果能返回的话就直接返回,如果返回的是null,那么就使用doCreateBean(这个过程和上面创建BeanPostProcessor的过程一致)去创建Bean实例了。那么这里我们也可以知道直接实现BeanPostProcessor接口的组件会在所有Bean创建实例的前后去起作用,而实现了InstantiationAwareBeanPostProcessor这个接口的组件是会在所有Bean创建实例之前有一个拦截,就是去尝试返回这个Bean的实例。换句话说就是我们这里的AnnotationAwareAspectJAutoProxyCreator会在其他所有Bean创建实例之前都会去尝试返回一个代理对象。
3.创建AOP代理对象
由于我们在创建Bean实例之前都会经过AbstractAutoProxyCreator(AnnotationAwareAspectJAutoProxyCreator的父类)的postProcessBeforeInstantiation方法去尝试得到一个实例,所以我们这里去看下该方法。
由于我们这里是看关于AOP对象的创建,所以我们把断点来到创建这些关于AOP对象的时候,先说下Demo关于AOP的类
需要被代理的对象:
增强方法:
配置类:
好了上面就是关于AOP的类了,我们继续看AbstractAutoProxyCreator的postProcessBeforeInstantiation方法
首先是判断需要初始化的这个Bean是否在adviseBeans里面,其次再判断这个Bean是否是基础类型的Advice,PointCut,Advisor,AopInfrastructureBean或者Aspect,如果我们准备初始化的类是需要被增强的Calculate,那么就返会false了,因为这个类不是上面的类型之一。
这里还有一个shouldSkip方法,它是已经被AnnotationAwareAspectJAutoProxyCreator所重写了
而父类AbstractAutoProxyCreator的shouldSkip方法是直接返回false的
所以这里综上所述,这里调用的shouldSkip结果应该是永远返回false了。
接着看下面的判断
由于返回了null,所以我们的容器就需要去创建这个Bean了,在创建完成这个Bean之后,就要经过我们的BeanPostProcessor(AbstractAutoProxyCreator)的postProcessAfterInitialization方法了
在里面主要的逻辑就是在wrapIfNecessary这个方法了,这个方法主要是创建代理对象的,所以我们进去看看里面到底是怎么创建的
接着看下面
这里有个找到能作用于当前创建的Bean的所有的增强器,深入看看
上面我们拿到了所有适用的增强器,然后接着看
重点就是createProxy这个方法了,看名字就知道这是创建代理对象的方法,深入去看
一直深入返回代理对象的方法,来到createAopProxy方法
最终我们拿到的就是一个被cglib或者被jdk创建的动态代理对象了。对该过程的总结就是,在创建AOP代理这个过程中,我们在初始化Bean之后(postProcessAfterInitialization方法)都会根据该Bean去找到对应的增强方法,如果有对应的增强方法就去为该对象去创建动态代理对象,如果没有的话就直接返回该对象,所以说之后我们在容器中获取到的就是这个组件的代理对象了。
4.获取拦截器链MethodInterceptor
首先我们在目标需要增强的方法打上断点debug看看
可以看到此时来到了一个intercept的方法 ,听名字大概知道这个方法是拦截目标方法的执行的,重点我们看下面创建拦截器链的逻辑
那么接下来我们主要看下该拦截器链是怎么创建的,进入创建拦截器链的方法
这里只是一些关于缓存的逻辑,主要创建拦截器链的是getInterceptorsAndDynamicInterceptionAdvice这个方法,进入该方法
首先我们可以看到主要的逻辑就是这个for循环中了,而遍历的是advisors这个集合,这个集合里面时有一个默认的ExposeInvocationInterceptor以及我们的4个增强器。而之后大概就是把每一个advisor都转换成interceptor
如果是MethodInterceptor类型的话就添加到集合中,如果不是的话就使用对应的AdvisorAdapter去转换成MethodInterceptor加入到集合中。所以遍历每一个advisor,然后就每一个advisor去获取对应得到的拦截器链数组,再把该数组放到拦截器容器中。
5.链式调用通知方法
那么上面我们得到了拦截器链,怎么去调用这个拦截器链里面的通知方法尼?通过上面我们知道当我们获取到的拦截器链不为空的时候,即有增强方法作用目标方法,那么就需要new一个CglibMethodInvocation去调用proceed去执行增强方法。我们来看下这个proceed方法是怎样执行的
首先上面是先进行判断当前拦截器链如果是空或者当前来到的拦截器的坐标索引等于拦截器链中的最后一个,那么就直接执行目标方法,然后再根据currentInterceptorIndex去拦截器链中拿到对应的拦截器(第一次拿到的就是ExposeInvocationInterceptor)。之后我们再看下面重点的((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this)这行代码,注意这里传了当前的ReflectiveMethodInvocation对象,然后我们深入进去看看
这里又把传过来的ReflectiveMethodInvocation对象调用proceed方法,即此时我们又回到了proceed方法,而此时就从拦截器链里面去拿到第二个拦截器了,所以说这个就相当于一层一层地去调用到下一个拦截器的invoke方法,那么我们这里就直接来看最后的一个拦截器MethodBeforeAdviceInterceptor
我们可以看到在执行proceed方法之前,有一个before方法,其实这个方法就是用来执行前置通知的,然后执行完前置通知我们再去执行proceed
此时来到proceed方法开始的判断条件可以判断出当前的拦截器是拦截器链中的最后一个拦截器了,那么就直接执行目标方法 ,所以我们可以看到日志符合了前置通知的结果
那么由于我们的拦截器的invoke方法是一层一层去调用的,那么当最后一个拦截器执行完了invoke方法后就继续返回到了倒数第二个拦截器的invoke方法,我们这里可以通过一张图来画出invoke方法的调用流程
6.AOP流程总结
首先从一开始的@EnableAspectJAutoProxy注解开启了AOP功能,目的就是给容器中导入AnnotationAwareAspectJAutoProxyCreator组件,而该组件又是一个BeanPostProcessor,在Spring容器初始化过程中,Spring的refresh方法中有一个registerBeanPostProcessors方法,该方法就是创建所有的BeanPostProcessor的,所以AnnotationAwareAspectJAutoProxyCreator组件就是在这里被创建的,之后来到finishBeanFactoryInitialization这个方法去创建剩余的单实例Bean,此时我们程序里面的业务逻辑组件包括我们的切面组件就会被创建,而在这些组件创建的过程前后,容器里面已经创建好了的BeanPostProcessor就会拦截并处理,其中我们的AnnotationAwareAspectJAutoProxyCreator组件会在创建Bean之前尝试返回一个Bean,如果不能返回就正常执行去创建这个Bean。在创建完这个Bean之后尼,AnnotationAwareAspectJAutoProxyCreator会在postProcessAfterInitialization方法(创建完Bean之后调用)中去判断该Bean是否需要创建一个代理对象,这里可以根据该类是否实现接口去使用JDK动态代理还是cglib动态代理,如果需要增强,即要创建代理对象,那么就把切入点以及该切入点的所有通知方法都包装成一个advisor增强器,再去创建一个代理对象。而在执行目标方法的时候,首先拿到目标方法的拦截器链,利用拦截器的链式调用机制,一次进入每一个拦截器进行执行增强方法。