SpringAOP原理学习笔记

93 阅读4分钟

前言

这里分析的是注解方式的aop, 流程图从网上找的,作为参考。

流程

image.png

image.png 图1:初始化advice过程

image.png 图2: 代理bean过程

image.png

图3:被aop代理的类被调用的执行过程

准备

添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>springAopTest</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>2.4.0</version>
        </dependency>
    </dependencies>
</project>

创建一个目标类

@Component
public class TargetClass {

    public String test(String value) {
        System.out.println("目标方法test被执行");
        if (!StringUtils.hasLength(value)) {
            throw new RuntimeException("value不能为空");
        }
        return value;
    }
}

编写切面类

@Aspect
@Component
public class MyAspect {

    @Pointcut("execution(public * 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()));
    }
}

拦截器执行顺序

  1. Spring4.x

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

    • 正常情况:@Before —-> 目标方法 —-> @AfterReturning —-> @After
    • 异常情况:@Before —-> 目标方法 —-> @AfterThrowing —-> @After

加载自动代理创建类

@EnableAspectJAutoProxy(自动加载)

在springboot中,通过@EnableAspectJAutoProxy,@import导入AspectJAutoProxyRegistrar,这个类加载了AnnotationAwareAspectJAutoProxyCreator。

image.png 图4:AnnotationAwareAspectJAutoProxyCreator继承关系图

通过AnnotationAwareAspectJAutoProxyCreator继承关系图中看出,这个是实现了BeanPostProcess接口,那么推测Bean代理可能发生在postProcessBeforeInstantiation方法或postProcessAfterInitialization方法。

生成代理对象

执行AnnotationAwareAspectJAutoProxyCreator

初始化之前

image.png

先看postProcessBeforeInitialization这个方法,这个方法主要看bean是不是advise(切面),如果是放入advicedBeans这个map中。(返回null标识继续初始化,如果返回对象则不进行初始化)

初始化之后

image.png

image.png

进入postProcessAfterInitialization方法,首先获取到当前bean对象匹配的切面数组, 在执行createProxy这里有一个SingletonTargetSource,这里把bean包装成为一个单例的代理对象。

image.png

image.png

image.png

然后把bean匹配的切面生成切面对象数组,还有把SingletonTargetSource(目标对象也就是我们的被代理的bean)set到ProxyFactory, 到createAopProxy方法中生成一个具体的Proxy,如果在spring配置中 @EnableAspectJAutoProxy(proxyTargetClass = true)或者spring.aop.proxy-target-class=true,则强制启用cglib来生成代理对象,即ObjenesisCglibAopProxy,如果bean实现了接口,则用JdkDynamicAopProxy,如果没有实现接口,则用ObjenesisCglibAopProxy

如何生成代理对象的

这里只是根据我自己的理解简单总结一下,以后有精力了详细看参考链接里的文章

JdkDynamicAopProxy

image.png

这里就是调用Proxy.newProxyInstance()方法,生成一个名为$Proxy的对象,这里通过重写编写了一个字节码文件加载到ClassLoader里,我们的原方法里面的内容则变成这个变成了InvocationHandler.invoke(), JdkDynamicAopProxy继承了InvocationHandler,所以实际执行的是JdkDynamicAopProxy.invoke()里面的方法。

参考链接:

Jdk动态代理 底层源码

JdkDynamicAopProxy

ObjenesisCglibAopProxy

这个比较复杂,也没看懂,放个参考链接

参考链接: spring aop创建代理对象

调用对象

CglibAopProxy

image.png

生成targetSource(目标对象)的方法拦截链(责任链模式),如果拦截链不为空,则创建一个CglibMethodInvocation对象,执行它的process方法。

image.png

image.png

调用父类ReflectiveMethodInvocation proceed方法,程序第一次进该方法时currentInterceptorIndex值为-1,this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex)取出拦截器链第一个拦截器ExposeInvocationInterceptor(默认拦截器,系统自动加上的),方法最后调用该拦截器的invoke方法。

image.png

拦截器链都是MethodInterceptor的实现类,常用的是MethodBeforeAdviceInterceptorAspectJAfterAdviceAfterReturningAdviceInterceptorAspectJAfterThrowingAdvice

image.png

这个这个mi其实是ReflectiveMethodInvocation,所以又会到了proceed方法,然后第二次是一个MethodBeforeAdviceInterceptor,再次执行invoke(this)

image.png

然后返回到ReflectiveMethodInvocation proceed方法, 然后第三次是一个AspectJAfterAdvice,在此调用invoke方法

image.png

再次此调用ReflectiveMethodInvocation.proceed()方法,

image.png

然后这时inde和拦截链的长度是一致的了,然后调用invokeJoinpoint()执行target(目标类方法),然后就会递归的方式来到AspectJAfterAdvice finally执行invokeAdviceMthod方法,然后回到MethodBeforeAdviceInterceptor,再回到ExposeInvocationInterceptor

image.png

最后回到CglibAopProxy.intercept方法,之后随之方法intercept方法执行结束,整个AOP的拦截器执行链也结束了。

总结

在SpringBoot中以注解的方式,首先通过通过@EnableAspectJAutoProxy@import导入AspectJAutoProxyRegistrar,这个类加载了AnnotationAwareAspectJAutoProxyCreator。 在Spring Bean 生命周期过程中,初始化之前执行postProcessBeforeInstantiation方法时把是advise(切面)放bean放入advisedBeans缓存中,初始化之后执行postProcessAfterInstantiation方法时把需要代理的bean,如果在spring配置中 @EnableAspectJAutoProxy(proxyTargetClass = true)或者spring.aop.proxy-target-class=true,则强制启用cglib来生成代理对象,即ObjenesisCglibAopProxy,如果bean实现了接口,则用JdkDynamicAopProxy,如果没有实现接口,则用ObjenesisCglibAopProxy,返回代理对象。

以Cglib代理为例,在实际调用target(目标代理对象)的方法,首先执行的是CglibAopProxy.DynamicAdvisedInterceptor.intercept方法,生成target的拦截链,拦截链都实现了MethodInterceptor接口的invoke方法,然后以递归的方式调用责任链的执行自定义的advice方法,最后完成Aop拦截器执行链。

参考链接:

深入理解Spring-AOP