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
导入了AspectJAutoProxyRegistrar
AspectJ自动代理注册器,查看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
,是默认的拦截器,后面会详细介绍。
剩下四个类型依次为:
MethodBeforeAdviceInterceptor
AspectJAfterAdvice
AfterReturningAdviceInterceptor
AspectJAfterThrowingAdvice
它们都是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
导入一个AspectJAutoProxyRegistrar
AspectJ自动代理注册器,这个类的作用是向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
方法,执行完毕后,拦截器链调用随之结束。