AOP(Aspect Oritented Programming):面向切面编程,通过预编译和运行期动态代理实现程序功能的唯一维护的一种技术。AOP是OOP的延续,也是对OOP的补充。它是Spring框架中的一个重要内容,是函数式变成的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,对MVC的垂直架构进行横向切入,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

代理模式

代理模式是GOF23种设计模式之一,为其他对象提供了一个代理以提供对某个对象的访问。

主要包括:

  1. 抽象接口
  2. 具体实现类
  3. 代理类

静态代理是代理模式的实现方式之一,是在程序运行前,由程序员创建或者特定工具自动生成源代码并对其编程生成.class文件。

对于静态代理而言,具体实现类与代理类是一一对应的,这也是广义上的代理模式特点。但是,如果抽象接口有K个实现类,那么也必然需要手动创建K个代理类,这便是静态代理的痛点。

相较于在编译期实现的静态代理,动态代理则在程序运行期实现。动态代理可以很方便地对委托类的相关方法进行统一增强处理。

下面以演唱会为场景进行演示静态代理和动态代理。

静态代理

静态代理的实现步骤如下:

  1. 定义业务接口;
  2. 创建业务接口实现类;
  3. 定义代理类并实现业务接口。

首先定义一个演唱会接口,定义演唱会的两个功能,开始和结束:

1
2
3
4
public interface Concert {
void start(String song);
void end(String song);
}

接着创建具体的演唱会实现类,在该类中真正实现演唱会的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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);
}
}

因为演唱会是需要场地举办,场地上要进行其他的广告插入和安全措施,通过代理创建最终的演唱会:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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("演唱会已经结束,请带走私人物品和垃圾,家长注意看好小孩");
}
}

最后编写测试代码:

1
2
3
4
5
6
7
8
9
10
public class StaticProxyTest {
public static void main(String[] args) {
// 业务实现类
Concert originConcert = new RealConcert("周杰伦");
// 目标代理类
Concert proxyConcert = new ProxyRealConcert(originConcert, "鸟巢");
proxyConcert.start("花海");
proxyConcert.end("安静");
}
}

运行,控制台输出如下:

1
2
3
4
5
6
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 - 演唱会已经结束,请带走私人物品和垃圾,家长注意看好小孩

可以看到,代理模式可以在不修改代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个抽象类。

动态代理

JDK动态代理需要使用到java.lang.reflect包下的三个类:InvocationHanlerMethodProxy

InvocationHanlerinvoke方法增强原实现类的功能,该方法有三个参数:

  1. Object proxy:JDK创建的代理对象;
  2. Method method:目标类中的方法;
  3. Object[] args:目标类中的参数。

ProxynewProxyInstance方法用于生成代理类,该方法有三个参数:

  • ClassLoader loader:目标类的类加载器,一般通过反射class.getClassLoader()获取;
  • Class[] interfaces:目标类实现的接口,一般通过反射class.getInterfaces()获取;
  • InvocationHandler h: 需要具体实现类实现这个接口,需要new出来当作参数。

动态代理的接口和被代理类与静态代理相同,只需要修改具体实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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("演唱会已经结束,请带走私人物品和垃圾,家长注意看好小孩");
}
}

测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
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中,切面的工作被称为通知。
通知定义了切面是什么以及何时使用,定义如下:

  1. 连接点(JpinPoint):在应用执行过程中能够插入切面的一个点。这个点是调用方法时,抛出异常时,甚至是修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
  2. 切点(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称来指定这些切点,或是利用正则表达式定义要匹配的类和方法名称来指定这些切点。
  3. 切面(Aspect):切面是通知和切点的结合。通知和切点共同定义了关于切面的全部内容——它是什么,在何时和何处完成其功能。
  4. 引入(Introdution):引入允许我们向现有的类添加新的方法和属性。
  5. 织入(Weaving):织入是将切面应用到目标对象来创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以进行织入:
    • 编译期:切面在目标类编译期时被织入
    • 类加载期:切面在目标类加载到JVM时被织入
    • 运行期:切面在应用运行的某个时候被织入(Spring使用)

Spring切面可以应用五种类型的通知:

  1. Before:在方法被调用之前调用通知
  2. After:在方法完成之后调用通知,无论方法是否执行成功
  3. After-returning:在方法成功执行之后调用通知
  4. After-throwing:在方法抛出异常之后调用通知
  5. Around:通知包括了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为

AOP的使用

新建项目,引入依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<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

1
2
3
4
5
6
7
8
9
10
11
@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;
}
}

编写切面类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@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个通知方法:

  1. 前置通知(@Before):在目标方法调用之前调用通知
  2. 后置通知(@After):在目标方法完成之后调用通知
  3. 返回通知(@AfterReturning):在目标方法成功执行之后通知
  4. 异常通知(@AfterThrowing):在目标方法抛出异常之后通知

通知是顺序在不同的Spring版本中会有所不同:

  • Spring4.x
    1. 正常情况:@Before —> 目标方法 —> @After —> @AfterReturning
    2. 异常情况:@Before —> 目标方法 —> @After —> @AfterThrowing
  • Spring5.x
    1. 正常情况:@Before —> 目标方法 —> @AfterReturning —> @After
    2. 异常情况:@Before —> 目标方法 —> @AfterThrowing —> @After

编写SpringBoot启动类:

1
2
3
4
5
6
7
8
9
@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("");
}
}

执行结果如下:

1
2
3
4
5
6
7
8
9
10
# 参数:"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自动代理,源码如下:

1
2
3
4
5
6
7
8
9
10
11
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

boolean proxyTargetClass() default false;

boolean exposeProxy() default false;

}

该注解类通过@Import导入了AspectJAutoProxyRegistrarAspectJ自动代理注册器,查看AspectJAutoProxyRegistrar源码:


这个类实现了ImportBeanDefinitionRegistrar接口,通过注释可以了解,这个注册器的作用是往IOC容器中注册一个AnnotationAwareAspectJAutoProxyCreator(注解驱动的AspectJ自动代理创建器)的Bean。
重点关注:

1
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

查看源码:

1
2
3
4
@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。

总结:@EnableAspectJAutoProxy模块驱动注解向IOC容器中注册了类型为AnnotationAwareAspectJAutoProxyCreator的bean。

AnnotationAwareAspectJAutoProxyCreator

通过前面的分析,我们的目光聚焦在AnnotationAwareAspectJAutoProxyCreator类上,为了搞清楚这个类的作用,先捋清类的层级关系:


可以看到AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator实现了SmartInstantiationAwareBeanPostProcessorBeanFactoryAware接口。实现BeanFactoryAware用于在Bean初始化时注入BeanFactory,而SmartInstantiationAwareBeanPostProcessor接口的父类为InstantiationAwareBeanPostProcessor,该接口继承自BeanPostProcesorBeanPostProcessorInstantiationAwareBeanProcessor接口包含一些用于Bean实例化和初始化前后进行自定义操作的方法,所以可以大体猜测出目标的代理是在这些接口方法中实现的。
查看AbstractAutoProxyCreator的源代码,它实现了BeanPostProcessorInstantiationAwareBeanProcessor接口,重写了InstantiationAwareBeanProcessorpostProcessBeforeInstantiation方法(自定义Bean实例化前的操作逻辑)和BeanPostProcessorpostProcessAfterInitialization方法(自定义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
2
/** Default is global AdvisorAdapterRegistry. */
private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();

advisedBeans用于存放Bean是否需要增强的标记,键为每个Bean的cacheKey,值为布尔类型,true表示需要增强,false表示不需要增强,此时TargetClass还未实例化,所以自然不在该集合中。
(3)判断当前Bean(TargetClass)是否基础类,查看AnnotationAwareAspectJAutoProxyCreatorisInfrastructureClass方法源码:


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


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


所以这一步的逻辑为:判断该当前Bean(TargetClass)是否是AdvicePointCutAdvisorAopInfrastructureBean的子类或者是否为切面类(被@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方法内部主要包含以下这些逻辑:

  1. 获取所有的通知方法(切面里定义的各个方法);
  2. 通过切点表达式判断这些通知方法是否为当前Bean所用;
  3. 如果有符合的通知方法,则对它们进行排序(排序规则不同版本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方法内部调用逻辑,会发现程序跳转到了CglibAopProxyintercept方法中,也就是说我们的目标对象的目标方法中,也就是说我们的目标对象的目标方法被CglibAopProxyintercept方法拦截了,该拦截方法主要逻辑如下:


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


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

继续查看this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice方法源码:


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


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

  • MethodBeforeAdviceInterceptor
  • AspectJAfterAdvice
  • AfterReturningAdviceInterceptor
  • AspectJAfterThrowingAdvice

它们都是MethodInterceptor的实现类:


链式调用通知方法

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


查看CglibMethodInvocation源码:


查看父类ReflectiveMethodInvocationproceed方法源码:


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


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


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


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


可以看到MethodBeforeInterceptorinvoke方法第一行调用通知方法before,此时控制台打印内容如下:

1
onBefore: test()开始执行,参数:[aop]

接着又通过mi.proceed再次调用ReflectiveMethodInvocationproceed方法,Step Into进入该方法:


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


可以看到AspectJAfterAdviceinvoke方法内通过mi.proceed再次调用ReflectiveMethodInvocationproceed方法,Step Into进入该方法:


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


可以看到AfterReturningAdviceInterceptorinvoke方法内通过mi.proceed再次调用ReflectiveMethodInvocationproceed方法,Step Into进入该方法:


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


可以看到AspectJAfterThrowingAdviceinvoke方法内通过mi.proceed再次调用ReflectiveMethodInvocationproceed方法,Step Into进入该方法:


此时程序第六次执行ReflectiveMethodInvocationproceed方法,currentInterceptorIndex值为4,条件程序,所以执行invokeJoinPoint方法,该方法内部通过反射调用了目标方法,执行后,控制台打印内容如下:

1
2
onBefore: test()开始执行,参数:[aop]
TargetClass: test()被执行

随着invokeJoinpoint()方法执行结束返回出战,程序回到AspectJAfterThrowingAdviceinvoke方法:


this.advice.afterReturning执行afterReturning通知方法,控制台打印内容如下:

1
2
3
onBefore: test()开始执行,参数:[aop]
TargetClass: test()被执行
onAfter: test()执行结束,参数:[aop]

AfterReturningAdviceInterceptorinvoke方法执行结束出栈,程序回到AspectJAfterAdviceinvoke方法:


AspectJAfterAdviceinvoke方法最终执行finally after逻辑,控制台打印内容如下:

1
2
3
4
onBefore: test()开始执行,参数:[aop]
TargetClass: test()被执行
onAfter: test()执行结束,参数:[aop]
afterReturning: test()执行返回,参数:[aop],返回值:newaop

AspectJAfterAdviceinvoke方法执行结束出栈,程序回到MethodBeforeAdviceInterceptorinvoke方法:


MethodBeforeAdviceInterceptorinvoke方法正常执行结束,出栈,程序回到ExposeInvocationInterceptorinvoke方法:


ExposeInvocationInterceptorinvoke方法执行结束出栈,程序回到CglibAopProxyintercept方法:


CglibAopProxyintercep执行完毕后,整个AOP的拦截器链调用也随之结束了。

下面用一张图总结拦截器链调用过程:


最终总结

(1)生成代理对象

@EnableAspectJAutoProxy模块驱动注解用于开启AspectJ自动代理,这个注解使用@Import导入一个AspectJAutoProxyRegistrarAspectJ自动代理注册器,这个类的作用是向IOC容器中注册AnnotationAwareAspectJAutoProxyCreator组件。

AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator重写了InstantiationAwareBeanPostProcessorpostProcessBeforeInstantiation方法和BeanPostProcessorpostProcessAfterInitialization方法。自定义Bean的实例化前逻辑中包含五个核心步骤,主要目的是进行一些校验,如果Bean本身就是切面类,那么就不会被代理了;如果我们自定义了TargetClass,则返回代理后的Bean。自定义Bean的初始化后逻辑中,主要进行wrapIfNecessary方法,先通过getAdvicesAndAdvisors方法获取Bean的通知方法集合并且进行排序,如果集合不为空,则通过createProxy创建该Bean的代理对象,最终会调用DefaultAopProxyFactorycreateAopProxy方法,这个方法会判断使用CGLIB还是JDK动态代理。

(如果代理对象没有实现接口或者实现的都是空接口,则使用CGLIB动态代理;默认使用JDK动态代理)

(2)生成拦截器链

目标方法会被CglibAopProxyintercept方法拦截,方法里会通过getInterceptorsAndDynamicInterceptionAdvice方法获取目标对象的拦截器链,创建目标方法的缓存key,首先从methodCache缓存中获取拦截器链,第一次调用时获取到的必然为空,那么则调用advisorChainFactorygetInterceptorsAndDynamicInterceptionAdvice方法封装拦截器链,并且将拦截器链存入缓存,此后调用直接从缓存中获取,提高AOP的性能。

『springboot2.4.0』(之前的版本后面四个拦截器顺序反转)最终生成的拦截器链的类型顺序:

  1. ExposeInvocationInterceptor(默认拦截器)

  2. MethodBeforeAdviceInterceptor(方法执行前拦截器)

  3. AspectJAfterAdvice(方法执行完成拦截器)

  4. AfterReturningAdviceInterceptor(方法执行成功拦截器)

  5. AspectJAfterThrowingAdvice(方法执行异常拦截器)

(3)链式调用通知

如果拦截器链为空,则直接通过反射执行目标方法;不为空则创建一个CglibMethodInvocation对象,它是ReflectiveMethodInvocation的子类,内部的proceed方法主要逻辑调用父类方法,内部维护了一个索引currentInterceptorIndex,初始值为-1,一步步取出拦截器链中的拦截器,并执行invoke方法,invoke方法中会通过参数this继续调用ReflectiveMethodInvocationproceed方法实现链式调用。

对于拦截器,ExposeInterceptorInvocation直接调用proceedMethodBeforeAdviceInterceptor先执行before再执行proceedAspectJAfterAdvice先调用proceed再执行invokeAdviceMethodAfterReturningAdviceInterceptor先调用proceed再执行afterReturningAspectJAfterThrowingAdvice先调用proceed再执行invokeAdviceMethod,先链式调用然后方法出栈,最后回到CglibAopProxyintercept方法,执行完毕后,拦截器链调用随之结束。