AOP(Aspect Oritented Programming):面向切面变成,通过预编译和运行期动态代理实现程序功能的唯一维护的一种技术。AOP是OOP的延续,也是对OOP的补充。它是Spring框架中的一个重要内容,是函数式变成的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,对MVC的垂直架构进行横向切入,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
代理模式
代理模式是GOF23种设计模式之一,为其他对象提供了一个代理以提供对某个对象的访问。
主要包括:
- 接口
- 被代理类
- 代理类
Java实现有静态代理和动态代理。静态代理在编译期实现,动态代理在运行期实现。
下面以演唱会为场景进行演示。
静态代理
首先需要定义一个演唱会接口,定义演唱会的两个功能,开始和结束:
public interface Concert {
void start(String song);
void end(String song);
}
接着创建真实的演唱会,在该类中真正实现演唱会的功能:
public class RealConcert implements Concert {
private Logger log = LoggerFactory.getLogger(this.getClass());
private String singerName;
public RealConcert(String singerName) {
this.singerName = singerName;
}
@Override
public void start(String song) {
log.info("{}演唱会即将开始", singerName);
log.info("第一首:《{}}》", song);
}
@Override
public void end(String song) {
log.info("最后一首:《{}》", song);
log.info("{}演唱会即将结束", singerName);
}
}
因为演唱会是需要场地举办,场地上要进行其他的广告插入和安全措施,通过代理创建最终的演唱会:
public class ProxyRealConcert implements Concert {
private final Logger log = LoggerFactory.getLogger(this.getClass());
private Concert concert;
private String address;
public ProxyRealConcert(Concert concert, String address) {
this.concert = concert;
this.address = address;
}
@Override
public void start(String song) {
startNotice();
concert.start(song);
}
@Override
public void end(String song) {
concert.end(song);
endNotice();
}
private void startNotice() {
log.info("欢迎来到{}看演唱会,荧光棒、粉丝牌9.8折,欢迎购买!", address);
}
private void endNotice() {
log.info("演唱会已经结束,请带走私人物品和垃圾,家长注意看好小孩");
}
}
最后编写测试代码:
public class StaticProxyTest {
public static void main(String[] args) {
// 被代理类
Concert originConcert = new RealConcert("周杰伦");
// 代理类
Concert proxyConcert = new ProxyRealConcert(originConcert, "鸟巢");
proxyConcert.start("花海");
proxyConcert.end("安静");
}
}
运行,控制台输出如下:
15:33:55.064 [main] INFO top.parak.quiet.SuperRealConcert - 欢迎来到鸟巢看演唱会,荧光棒、粉丝牌9.8折,欢迎购买!
15:33:55.067 [main] INFO top.parak.quiet.RealConcert - 周杰伦演唱会即将开始
15:33:55.067 [main] INFO top.parak.quiet.RealConcert - 第一首:《花海》
15:33:55.067 [main] INFO top.parak.quiet.RealConcert - 最后一首:《安静》
15:33:55.067 [main] INFO top.parak.quiet.RealConcert - 周杰伦演唱会即将结束
15:33:55.067 [main] INFO top.parak.quiet.SuperRealConcert - 演唱会已经结束,请带走私人物品和垃圾,家长注意看好小孩
可以看到,代理模式可以在不修改代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个抽象类。
动态代理
接口和被代理类与静态代理相同,修改代理类:
public class ProxyRealConcert implements InvocationHandler {
private final Logger log = LoggerFactory.getLogger(this.getClass());
private Object concert;
private String address;
public ProxyRealConcert(Object concert, String address) {
this.concert = concert;
this.address = address;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("start")) {
startNotice();
method.invoke(concert, args);
} else if (method.getName().equals("end")) {
method.invoke(concert, args);
endNotice();
}
return null;
}
private void startNotice() {
log.info("欢迎来到{}看演唱会,荧光棒、粉丝牌9.8折,欢迎购买!", address);
}
private void endNotice() {
log.info("演唱会已经结束,请带走私人物品和垃圾,家长注意看好小孩");
}
}
测试代码如下:
public class DynamicProxyTest {
public static void main(String[] args) {
// 被代理类
Concert originConcert = new RealConcert("周杰伦");
InvocationHandler invocationHandler = new ProxyRealConcert(originConcert, " 鸟巢");
// 代理类
Concert proxyConcert = (Concert) Proxy.newProxyInstance(RealConcert.class.getClassLoader(), RealConcert.class.getInterfaces(), invocationHandler);
proxyConcert.start("花海");
proxyConcert.end("安静");
}
}
运行启动类,实现效果与静态代理相同。
AOP的术语
在AOP中,切面的工作被称为通知。
通知定义了切面是什么以及何时使用,定义如下:
- 连接点(JpinPoint):在应用执行过程中能够插入切面的一个点。这个点是调用方法时,抛出异常时,甚至是修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
- 切点(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称来指定这些切点,或是利用正则表达式定义要匹配的类和方法名称来指定这些切点。
- 切面(Aspect):切面是通知和切点的结合。通知和切点共同定义了关于切面的全部内容——它是什么,在何时和何处完成其功能。
- 引入(Introdution):引入允许我们向现有的类添加新的方法和属性。
- 织入(Weaving):织入是将切面应用到目标对象来创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以进行织入:
- 编译期:切面在目标类编译期时被织入
- 类加载期:切面在目标类加载到JVM时被织入
- 运行期:切面在应用运行的某个时候被织入(Spring使用)
Spring切面可以应用五种类型的通知:
- Before:在方法被调用之前调用通知
- After:在方法完成之后调用通知,无论方法是否执行成功
- After-returning:在方法成功执行之后调用通知
- After-throwing:在方法抛出异常之后调用通知
- Around:通知包括了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
AOP的使用
新建项目,引入依赖:
<properties>
<spring.boot.version>2.4.0</spring.boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>${spring.boot.version}</version>
</dependency>
</dependencies>
创建一个目标类,里面包含需要被AOP代理增强的方法test
:
@Component
public class TargetClass {
public String test(String value) {
System.out.println(this.getClass().getSimpleName() + ": test()被执行");
if (!StringUtils.hasLength(value)) {
throw new RuntimeException("value不能为空");
}
return "new" + value;
}
}
编写切面类:
@Aspect
@Component
public class MyAspect {
@Pointcut("execution(public * top.parak.TargetClass.test(..))")
public void pointcut() {
}
@Before("pointcut()")
public void onBefore(JoinPoint joinPoint) {
System.out.println("onBefore: " + joinPoint.getSignature().getName() +
"()开始执行,参数:" + Arrays.asList(joinPoint.getArgs()));
}
@After("pointcut()")
public void onAfter(JoinPoint joinPoint) {
System.out.println("onAfter: " + joinPoint.getSignature().getName() +
"()执行结束,参数:" + Arrays.asList(joinPoint.getArgs()));
}
@AfterReturning(value = "pointcut()", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
System.out.println("afterReturning: " + joinPoint.getSignature().getName() +
"()执行返回,参数:" + Arrays.asList(joinPoint.getArgs()) + ",返回值:" + result);
}
@AfterThrowing(value = "pointcut()", throwing = "exception")
public void afterThrowing(JoinPoint joinPoint, Exception exception) {
System.out.println("afterThrowing: " + joinPoint.getSignature().getName() +
"()执行出错,参数:" + Arrays.asList(joinPoint.getArgs()) + ",异常:" + exception.getMessage());
}
}
该切面包含了4个通知方法:
- 前置通知(@Before):在目标方法调用之前调用通知
- 后置通知(@After):在目标方法完成之后调用通知
- 返回通知(@AfterReturning):在目标方法成功执行之后通知
- 异常通知(@AfterThrowing):在目标方法抛出异常之后通知
通知是顺序在不同的Spring版本中会有所不同:
- Spring4.x
- 正常情况:@Before —> 目标方法 —> @After —> @AfterReturning
- 异常情况:@Before —> 目标方法 —> @After —> @AfterThrowing
- Spring5.x
- 正常情况:@Before —> 目标方法 —> @AfterReturning —> @After
- 异常情况:@Before —> 目标方法 —> @AfterThrowing —> @After
编写SpringBoot启动类:
@SpringBootApplication
public class AOPApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(AOPApplication.class, args);
TargetClass targetClass = applicationContext.getBean(TargetClass.class);
targetClass.test("aop");
targetClass.test("");
}
}
执行结果如下:
# 参数:"aop"
onBefore: test()开始执行,参数:[aop]
TargetClass: test()被执行
afterReturning: test()执行返回,参数:[aop],返回值:newaop
onAfter: test()执行结束,参数:[aop]
# 参数:""
onBefore: test()开始执行,参数:[]
TargetClass: test()被执行
afterThrowing: test()执行出错,参数:[],异常:value不能为空
onAfter: test()执行结束,参数:[]
@EnableAspectAutoProxy
在前面引入的spring-boot-starter-aop
中,@Enable模块驱动注解@EnableAspectJAutoProxy
用于开启AspectJ自动代理,源码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
该注解类通过@Import
导入了AspectJAutoProxyRegistrar
AspectJ自动代理注册器,查看AspectJAutoProxyRegistrar
源码:
这个类实现了ImportBeanDefinitionRegistrar
接口,通过注释可以了解,这个注册器的作用是往IOC容器中注册一个AnnotationAwareAspectJAutoProxyCreator
(注解驱动的AspectJ自动代理创建器)的Bean。
重点关注:
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
查看源码:
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
}
实际上调用下面方法:
可以看到,核心逻辑为通过RootBeanDefinition
向IOC注册了名称为AUTO_PROXY_CREATOR_BEAN_NAME
(常量,值为org.springframework.aop.config.internalAutoProxyCreator
),类型为AnnotationAwareAspectJAutoProxyCreator
的Bean。
0p’lo
总结:@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的成员变量)
/** Default is global AdvisorAdapterRegistry. */
private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
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
,此时控制台打印内容如下:
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
方法,该方法内部通过反射调用了目标方法,执行后,控制台打印内容如下:
onBefore: test()开始执行,参数:[aop]
TargetClass: test()被执行
随着invokeJoinpoint()
方法执行结束返回出战,程序回到AspectJAfterThrowingAdvice
的invoke
方法:this.advice.afterReturning
执行afterReturning
通知方法,控制台打印内容如下:
onBefore: test()开始执行,参数:[aop]
TargetClass: test()被执行
onAfter: test()执行结束,参数:[aop]
AfterReturningAdviceInterceptor
的invoke
方法执行结束出栈,程序回到AspectJAfterAdvice
的invoke
方法:AspectJAfterAdvice
的invoke
方法最终执行finally after逻辑,控制台打印内容如下:
onBefore: test()开始执行,参数:[aop]
TargetClass: test()被执行
onAfter: test()执行结束,参数:[aop]
afterReturning: test()执行返回,参数:[aop],返回值:newaop
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
方法主要逻辑是调用父类的proceed
方法,内部维护了一个索引currentInterceptorIndex
,初始值为-1,一步步取出拦截器链中的拦截器,并执行invoke
方法,invoke
方法中会通过参数this
继续调用ReflectiveMethodInvocation
的proceed
方法实现链式调用。
对于拦截器,ExposeInterceptorInvocation
直接调用proceed
,MethodBeforeAdviceInterceptor
先执行before
再执行proceed
,AspectJAfterAdvice
先调用proceed
再执行invokeAdviceMethod
,AfterReturningAdviceInterceptor
先调用proceed
再执行afterReturning
,AspectJAfterThrowingAdvice
先调用proceed
再执行invokeAdviceMethod
,先链式调用然后方法出栈,最后回到CglibAopProxy
的intercept
方法,执行完毕后,拦截器链调用随之结束。