Spring-aop原理
AOP(Aspect Oritented Programming):面向切面编程,通过预编译和运行期动态代理实现程序功能的唯一维护的一种技术。AOP是OOP的延续,也是对OOP的补充。它是Spring框架中的一个重要内容,是函数式变成的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,对MVC的垂直架构进行横向切入,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
代理模式
代理模式是GOF23种设计模式之一,为其他对象提供了一个代理以提供对某个对象的访问。
主要包括:
- 抽象接口
- 具体实现类
- 代理类
静态代理是代理模式的实现方式之一,是在程序运行前,由程序员创建或者特定工具自动生成源代码并对其编程生成.class文件。
对于静态代理而言,具体实现类与代理类是一一对应的,这也是广义上的代理模式特点。但是,如果抽象接口有K个实现类,那么也必然需要手动创建K个代理类,这便是静态代理的痛点。
相较于在编译期实现的静态代理,动态代理则在程序运行期实现。动态代理可以很方便地对委托类的相关方法进行统一增强处理。
下面以演唱会为场景进行演示静态代理和动态代理。
静态代理
静态代理的实现步骤如下:
- 定义业务接口;
- 创建业务接口实现类;
- 定义代理类并实现业务接口。
首先定义一个演唱会接口,定义演唱会的两个功能,开始和结束:
1 | public interface Concert { |
接着创建具体的演唱会实现类,在该类中真正实现演唱会的功能:
1 | public class RealConcert implements Concert { |
因为演唱会是需要场地举办,场地上要进行其他的广告插入和安全措施,通过代理创建最终的演唱会:
1 | public class ProxyRealConcert implements Concert { |
最后编写测试代码:
1 | public class StaticProxyTest { |
运行,控制台输出如下:
1 | 15:33:55.064 [main] INFO top.parak.quiet.SuperRealConcert - 欢迎来到鸟巢看演唱会,荧光棒、粉丝牌9.8折,欢迎购买! |
可以看到,代理模式可以在不修改代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个抽象类。
动态代理
JDK动态代理需要使用到java.lang.reflect包下的三个类:InvocationHanler、Method和Proxy。
InvocationHanler的invoke方法增强原实现类的功能,该方法有三个参数:
Object proxy:JDK创建的代理对象;Method method:目标类中的方法;Object[] args:目标类中的参数。
Proxy的newProxyInstance方法用于生成代理类,该方法有三个参数:
ClassLoader loader:目标类的类加载器,一般通过反射class.getClassLoader()获取;Class[] interfaces:目标类实现的接口,一般通过反射class.getInterfaces()获取;InvocationHandler h: 需要具体实现类实现这个接口,需要new出来当作参数。
动态代理的接口和被代理类与静态代理相同,只需要修改具体实现类:
1 | public class ProxyRealConcert implements InvocationHandler { |
测试代码如下:
1 | public class DynamicProxyTest { |
运行启动类,实现效果与静态代理相同。
AOP的术语
在AOP中,切面的工作被称为通知。
通知定义了切面是什么以及何时使用,定义如下:
- 连接点(JpinPoint):在应用执行过程中能够插入切面的一个点。这个点是调用方法时,抛出异常时,甚至是修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
- 切点(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称来指定这些切点,或是利用正则表达式定义要匹配的类和方法名称来指定这些切点。
- 切面(Aspect):切面是通知和切点的结合。通知和切点共同定义了关于切面的全部内容——它是什么,在何时和何处完成其功能。
- 引入(Introdution):引入允许我们向现有的类添加新的方法和属性。
- 织入(Weaving):织入是将切面应用到目标对象来创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以进行织入:
- 编译期:切面在目标类编译期时被织入
- 类加载期:切面在目标类加载到JVM时被织入
- 运行期:切面在应用运行的某个时候被织入(Spring使用)
Spring切面可以应用五种类型的通知:
- Before:在方法被调用之前调用通知
- After:在方法完成之后调用通知,无论方法是否执行成功
- After-returning:在方法成功执行之后调用通知
- After-throwing:在方法抛出异常之后调用通知
- Around:通知包括了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
AOP的使用
新建项目,引入依赖:
1 | <properties> |
创建一个目标类,里面包含需要被AOP代理增强的方法test:
1 |
|
编写切面类:
1 |
|
该切面包含了4个通知方法:
- 前置通知(@Before):在目标方法调用之前调用通知
- 后置通知(@After):在目标方法完成之后调用通知
- 返回通知(@AfterReturning):在目标方法成功执行之后通知
- 异常通知(@AfterThrowing):在目标方法抛出异常之后通知
通知是顺序在不同的Spring版本中会有所不同:
- Spring4.x
- 正常情况:@Before —> 目标方法 —> @After —> @AfterReturning
- 异常情况:@Before —> 目标方法 —> @After —> @AfterThrowing
- Spring5.x
- 正常情况:@Before —> 目标方法 —> @AfterReturning —> @After
- 异常情况:@Before —> 目标方法 —> @AfterThrowing —> @After
编写SpringBoot启动类:
1 |
|
执行结果如下:
1 | # 参数:"aop" |
@EnableAspectAutoProxy
在前面引入的spring-boot-starter-aop中,@Enable模块驱动注解@EnableAspectJAutoProxy用于开启AspectJ自动代理,源码如下:
1 |
|
该注解类通过@Import导入了AspectJAutoProxyRegistrarAspectJ自动代理注册器,查看AspectJAutoProxyRegistrar源码:

这个类实现了ImportBeanDefinitionRegistrar接口,通过注释可以了解,这个注册器的作用是往IOC容器中注册一个AnnotationAwareAspectJAutoProxyCreator(注解驱动的AspectJ自动代理创建器)的Bean。
重点关注:
1 | AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); |
查看源码:
1 |
|
实际上调用下面方法:

可以看到,核心逻辑为通过RootBeanDefinition向IOC注册了名称为AUTO_PROXY_CREATOR_BEAN_NAME(常量,值为org.springframework.aop.config.internalAutoProxyCreator),类型为AnnotationAwareAspectJAutoProxyCreator的Bean。
总结:@EnableAspectJAutoProxy模块驱动注解向IOC容器中注册了类型为
AnnotationAwareAspectJAutoProxyCreator的bean。
AnnotationAwareAspectJAutoProxyCreator
通过前面的分析,我们的目光聚焦在AnnotationAwareAspectJAutoProxyCreator类上,为了搞清楚这个类的作用,先捋清类的层级关系:

可以看到AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator实现了SmartInstantiationAwareBeanPostProcessor和BeanFactoryAware接口。实现BeanFactoryAware用于在Bean初始化时注入BeanFactory,而SmartInstantiationAwareBeanPostProcessor接口的父类为InstantiationAwareBeanPostProcessor,该接口继承自BeanPostProcesor。BeanPostProcessor和InstantiationAwareBeanProcessor接口包含一些用于Bean实例化和初始化前后进行自定义操作的方法,所以可以大体猜测出目标的代理是在这些接口方法中实现的。
查看AbstractAutoProxyCreator的源代码,它实现了BeanPostProcessor和InstantiationAwareBeanProcessor接口,重写了InstantiationAwareBeanProcessor的postProcessBeforeInstantiation方法(自定义Bean实例化前的操作逻辑)和BeanPostProcessor的postProcessAfterInitialization方法(自定义Bean初始化后的操作逻辑):

所以在这两个方法上打上两个断点,用于后续Debug:

AOP创建代理过程
使用Debug方式启动上述AOP的Demo,因为后置处理器对所有Bean都生效,所以每个Bean创建时都会进入我们刚刚打断点的两个方法中。但我们只关心Spring AOP是如何增强我们的目标类TargetClass的,所以如果Bean类型不是TargetClass,我们直接点击Resume Program跳过,知道Bean类型是TargetClass:

postProcessBeforeInstantiation方法主要包含以下几个核心步骤:

(1)通过Bean名称和Bean类型获取该Bean的唯一缓存键名,getCache方法源码如下:

在这里,cacheKey的值为targetClass。
(2)判断当前Bean(TargetClass)是否包含在advisedBeans集合中(AbstractAutoProxyCreator的成员变量)
1 | /** Default is global AdvisorAdapterRegistry. */ |
advisedBeans用于存放Bean是否需要增强的标记,键为每个Bean的cacheKey,值为布尔类型,true表示需要增强,false表示不需要增强,此时TargetClass还未实例化,所以自然不在该集合中。
(3)判断当前Bean(TargetClass)是否基础类,查看AnnotationAwareAspectJAutoProxyCreator的isInfrastructureClass方法源码:

其中isInfrastructureClass调用父类方法,如下:

this.aspectAdvisorFactory.isAspect方法源码如下:

所以这一步的逻辑为:判断该当前Bean(TargetClass)是否是Advice、PointCut、Advisor、AopInfrastructureBean的子类或者是否为切面类(被@Aspect注解标注)。
(4)判断是否需要跳过,shouldSkip方法源码如下:


通过Bean名称判断是否以AutowireCapableBeanFactory._ORIGINAL_INSTANCE_SUFFIX_(.ORIGINAL)结尾,是的话返回true代表跳过处理。
显然,TargetClass不符合3和4,继续走第5步。
(5)如果我们自定义了TargetSource,则在此处理Bean代理,以取代目标Bean的后续默认实例化方式。我们并没有自定义TargetClass,所以直接跳过。
经过以上五步,就TargetClass这个Bean而言,postProcessBeforeInstantiation方法最终返回null。Bean实例化前置处理到此完毕,点击Resume Program,继续Bean的后续生命周期处理逻辑,程序跳转到Bean初始化后置处理方法postProcessAfterInitialization:

重点关注wrapIfNecessary方法,查看wrapIfNecessary方法源码:

(1)getAdvicesAndAdvisors方法内部主要包含以下这些逻辑:
- 获取所有的通知方法(切面里定义的各个方法);
- 通过切点表达式判断这些通知方法是否为当前Bean所用;
- 如果有符合的通知方法,则对它们进行排序(排序规则不同版本Spring有所不同,上面已经提及)。
在前面的AOP例子中,切面MyAspect里的通知方法就是为了增强TargetClass所设的(根据切点表达式),所以getAdvicesAndAdvisorsForBean方法返回值如下所示:

这些通知方法就是我们在MyAspect切面里定义的通知方法。
(2)如果该Bean的通知方法集合不为空的话,则创建该Bean的代理对象,具体查看createProxy方法源码:

继续跟踪proxyFactory.getProxy(getProxyClassLoader())源码:



Spring会判断当前使用哪种代理对象(一般来说Bean有实现接口时,使用JDK动态代理,当Bean没有实现接口时,使用Cglib动态代理。在SpirngBoot中,我们可以通过spring.aop.proxy-target-class=true配置来强制使用Cglib代理)。

后续从IOC容器中获得的TargetClass就是被代理后的对象,执行代理对象的目标方法的时候,代理对象会执行相应的通知方法链。
生成拦截器链
AOP代理对象生成后,我们接着关注代理对象的目标方法执行时,通知方式是怎么被执行的。
先将前面的所有断点去掉,然后在AOPApplication的如下位置打上断点:

再次以Debug方式启动程序,可以看到获取到的TargetClass就是前面Cglib代理后的Bean:

点击Step Into进入test方法内部调用逻辑,会发现程序跳转到了CglibAopProxy的intercept方法中,也就是说我们的目标对象的目标方法中,也就是说我们的目标对象的目标方法被CglibAopProxy的intercept方法拦截了,该拦截方法主要逻辑如下:

这里重点关注getInterceptorsAndDynamicInterceptionAdvice方法,源码如下:

所谓的拦截器链,就是在代理对象的某个方法被执行时,从通知方法集合(创建代理对象的时候就已经通知集合保存在代理对象中了)中筛选出适用于该方法的通知,然后封装为拦截器对象集合(类型为MethodInterceptor)。
继续查看this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice方法源码:

在intercept方法的this.advised.getInterceptorsAndDynamicInterceptionAdvice_(_method, targetClass_)_这一行打个断点,点击Resume Program,看到拦截器链的内容如下:

拦截器链第一个元素类型为ExposeInvocationInterceptor,是默认的拦截器,后面会详细介绍。
剩下四个类型依次为:
MethodBeforeAdviceInterceptorAspectJAfterAdviceAfterReturningAdviceInterceptorAspectJAfterThrowingAdvice
它们都是MethodInterceptor的实现类:

链式调用通知方法
获取到了代理对象目标方法的拦截器链后,我们最后来关注这些拦截器是如何链式调用通知方法的。获取拦截器链并且拦截器链不为空时,CglibAopProxy的intercept方法创建CglibMethodInvoation对象,并调用它的proceed方法:

查看CglibMethodInvocation源码:

查看父类ReflectiveMethodInvocation的proceed方法源码:

清除之前打的所有断点,在该方法上第一行打个断点,重新以Debug方式启动程序:

程序第一次进入该方法时currentInterceptorIndex值为-1,this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex)取出拦截器链第一个拦截器ExposeInvocationInterceptor,方法最后调用该拦截器的invoke方法,点击Step into,进入ExposeInvocationInterceptor的invoke方法:

mi就是我们传入的ReflectiveMethodInvocation对象,程序执行到mi.proceed方法时,Step Into进入该方法:

可以看到,此时程序第二次执行ReflectiveMethodInvocation的proceed方法,currentInterceptorIndex值为0,this.interceptorsAndDynamicMethodMatchers.get_(_++this.currentInterceptorIndex_)_取拦截器链第二个拦截器MethodBeforeInterceptor,方法最后调用该拦截器的invoke方法,Step Into进入该方法:

可以看到MethodBeforeInterceptor的invoke方法第一行调用通知方法before,此时控制台打印内容如下:
1 | onBefore: test()开始执行,参数:[aop] |
接着又通过mi.proceed再次调用ReflectiveMethodInvocation的proceed方法,Step Into进入该方法:

此时程序第三次执行ReflectiveMethodInvocation的proceed方法,currentInterceptorIndex值为1,this.interceptorsAndDynamicMethodMatchers.get_(_++this.currentInterceptorIndex_)_取拦截器链第三个拦截器AspectJAfterAdvice,方法最后调用该拦截器的invoke方法,Step Into进入该方法:

可以看到AspectJAfterAdvice的invoke方法内通过mi.proceed再次调用ReflectiveMethodInvocation的proceed方法,Step Into进入该方法:

此时程序第四次执行ReflectiveMethodInvocation的proceed方法,currentInterceptorIndex的值为2,this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex)取出拦截器链第四个拦截器AfterReturningAdviceInterceptor,方法最后调用该拦截器的invoke方法,Step Into进入该方法:

可以看到AfterReturningAdviceInterceptor的invoke方法内通过mi.proceed再次调用ReflectiveMethodInvocation的proceed方法,Step Into进入该方法:

此时程序第五次执行ReflectiveMethodInvocation的proceed方法,currentInterceptorIndex值为3,this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex)取出拦截器链第五个拦截器AspectJAfterThrowingAdvice,方法最后调用该拦截器的invoke方法,Step Into进入该方法:

可以看到AspectJAfterThrowingAdvice的invoke方法内通过mi.proceed再次调用ReflectiveMethodInvocation的proceed方法,Step Into进入该方法:

此时程序第六次执行ReflectiveMethodInvocation的proceed方法,currentInterceptorIndex值为4,条件程序,所以执行invokeJoinPoint方法,该方法内部通过反射调用了目标方法,执行后,控制台打印内容如下:
1 | onBefore: test()开始执行,参数:[aop] |
随着invokeJoinpoint()方法执行结束返回出战,程序回到AspectJAfterThrowingAdvice的invoke方法:

this.advice.afterReturning执行afterReturning通知方法,控制台打印内容如下:
1 | onBefore: test()开始执行,参数:[aop] |
AfterReturningAdviceInterceptor的invoke方法执行结束出栈,程序回到AspectJAfterAdvice的invoke方法:

AspectJAfterAdvice的invoke方法最终执行finally after逻辑,控制台打印内容如下:
1 | onBefore: test()开始执行,参数:[aop] |
AspectJAfterAdvice的invoke方法执行结束出栈,程序回到MethodBeforeAdviceInterceptor的invoke方法:

MethodBeforeAdviceInterceptor的invoke方法正常执行结束,出栈,程序回到ExposeInvocationInterceptor的invoke方法:

ExposeInvocationInterceptor的invoke方法执行结束出栈,程序回到CglibAopProxy的intercept方法:

CglibAopProxy的intercep执行完毕后,整个AOP的拦截器链调用也随之结束了。
下面用一张图总结拦截器链调用过程:

最终总结
(1)生成代理对象
@EnableAspectJAutoProxy模块驱动注解用于开启AspectJ自动代理,这个注解使用@Import导入一个AspectJAutoProxyRegistrarAspectJ自动代理注册器,这个类的作用是向IOC容器中注册AnnotationAwareAspectJAutoProxyCreator组件。
AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator重写了InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation方法和BeanPostProcessor的postProcessAfterInitialization方法。自定义Bean的实例化前逻辑中包含五个核心步骤,主要目的是进行一些校验,如果Bean本身就是切面类,那么就不会被代理了;如果我们自定义了TargetClass,则返回代理后的Bean。自定义Bean的初始化后逻辑中,主要进行wrapIfNecessary方法,先通过getAdvicesAndAdvisors方法获取Bean的通知方法集合并且进行排序,如果集合不为空,则通过createProxy创建该Bean的代理对象,最终会调用DefaultAopProxyFactory的createAopProxy方法,这个方法会判断使用CGLIB还是JDK动态代理。
(如果代理对象没有实现接口或者实现的都是空接口,则使用CGLIB动态代理;默认使用JDK动态代理)
(2)生成拦截器链
目标方法会被CglibAopProxy的intercept方法拦截,方法里会通过getInterceptorsAndDynamicInterceptionAdvice方法获取目标对象的拦截器链,创建目标方法的缓存key,首先从methodCache缓存中获取拦截器链,第一次调用时获取到的必然为空,那么则调用advisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice方法封装拦截器链,并且将拦截器链存入缓存,此后调用直接从缓存中获取,提高AOP的性能。
『springboot2.4.0』(之前的版本后面四个拦截器顺序反转)最终生成的拦截器链的类型顺序:
ExposeInvocationInterceptor(默认拦截器)MethodBeforeAdviceInterceptor(方法执行前拦截器)AspectJAfterAdvice(方法执行完成拦截器)AfterReturningAdviceInterceptor(方法执行成功拦截器)AspectJAfterThrowingAdvice(方法执行异常拦截器)
(3)链式调用通知
如果拦截器链为空,则直接通过反射执行目标方法;不为空则创建一个CglibMethodInvocation对象,它是ReflectiveMethodInvocation的子类,内部的proceed方法主要逻辑调用父类方法,内部维护了一个索引currentInterceptorIndex,初始值为-1,一步步取出拦截器链中的拦截器,并执行invoke方法,invoke方法中会通过参数this继续调用ReflectiveMethodInvocation的proceed方法实现链式调用。
对于拦截器,ExposeInterceptorInvocation直接调用proceed,MethodBeforeAdviceInterceptor先执行before再执行proceed,AspectJAfterAdvice先调用proceed再执行invokeAdviceMethod,AfterReturningAdviceInterceptor先调用proceed再执行afterReturning,AspectJAfterThrowingAdvice先调用proceed再执行invokeAdviceMethod,先链式调用然后方法出栈,最后回到CglibAopProxy的intercept方法,执行完毕后,拦截器链调用随之结束。





