SpringAOP专栏二《原理篇》

上一篇SpringAOP专栏一《使用教程篇》-CSDN博客介绍了SpringAop如何使用,这一篇文章就会介绍Spring AOP 的底层实现原理,并通过源代码解析来详细阐述其实现过程。

前言

Spring AOP 的实现原理是基于动态代理字节码操作的。不了解动态代理和字节码操作的读者可以先看一下这篇文章java中的反射和代理模式-CSDN博客

实现原理

下面我会基于在使用SpringAOP进行逻辑增强时各个核心类的执行顺序进行底层原理的剖析

实现代理和切面逻辑的核心类执行顺序如下:

1.Bean 实例化阶段:

  • BeanPostProcessor:在 Bean 实例化之后,进行初始化前的处理。其中,AbstractAutoProxyCreator 是一个重要的 BeanPostProcessor 实现类,用于自动创建代理对象。

2.切面织入阶段:

  • AdvisedSupport:封装了切面逻辑和目标对象信息。
  • ProxyFactoryBean:生成代理对象的工厂类。
  • AopProxyFactory:AOP 代理对象的工厂接口。
  • AopProxy:AOP 代理对象的核心接口,定义了获取代理对象的方法。
  • CglibAopProxy:基于 CGLIB 的动态代理实现类。
  • DynamicAdvisedInterceptor:CGLIB 动态代理的回调函数实现类,用于触发切面逻辑的执行。

3.方法拦截与执行阶段:

  • ProxyMethodInvocation:代理方法调用的核心类,封装了目标对象方法和参数信息。
  • ReflectiveMethodInvocation:反射调用目标方法的类,是 ProxyMethodInvocation 的具体实现类。
  • MethodInvocationInterceptor:方法拦截器接口,定义了拦截器的方法执行逻辑。
  • MethodInterceptor:CGLIB 库中的接口,被 MethodInvocationInterceptor 实现。

Bean 实例化阶段

在service bean的创建过程中(也就是getBean("service")),AOP通过BeanPostProcess后置处理器操作进行介入 分为2种情况:

  • 用户自定义了targetSource,则bean的创建(实例化、填充、初始化)均由用户负责,Spring Ioc不会在管该代理目标对象traget,这种情况基本上不会发生,很多人用了几年Spring可能都不知道有它的存在
  • 正常情况下都是Spring Ioc完成代理对象target的实例化、填充、初始化。然后在初始化后置处理器中进行介入,对bean也就是service进行代理

下面是Bean示例化的流程图

SpringAOP专栏二《原理篇》_第1张图片

切面织入阶段

Spring AOP 是构建在动态代理基础上,因此 Spring 对 AOP 的支持局限于方法级别的拦截。

在这个阶段会读取相关的SpringAOP的配置,如何将切面逻辑和目标对象信息封装到AdvisedSupport中,再通过ProxyFactoryBean(生成代理对象的工厂类)根据目标对象是否实现接口来调用JDK动态代理还是Cglib代理。

下面详细介绍一下两者代理方式的实现源码:

Spring AOP 动态代理实现:

  • 默认情况下,实现了接⼝的类,使⽤ AOP 会基于 JDK ⽣成代理类,没有实现接⼝的类,会基于 CGLIB ⽣成代理类。
  • JDK Proxy(JDK 动态代理)
  • CGLIB Proxy:默认情况下 Spring AOP 都会采用 CGLIB 来实现动态代理,因为效率高
  • CGLIB 实现原理:通过继承代理对象来实现动态代理的(子类拥有父类的所有功能)
  • CGLIB 缺点:不能代理最终类(也就是被 final 修饰的类)
     

JDK动态代理

    JDK 动态代理是 Java 自带的动态代理实现方式。使用JDK动态代理时,需要目标对象实现至少一个接口。JDK 动态代理会在运行时生成一个实现了目标对象接口的代理类,该代理类会在目标对象方法执行前后插入切面代码。

下面是JdkDynamicAopProxy类的部分源码,感兴趣的读者可以自己去看看,我这里就不一一截图给大家看了。

SpringAOP专栏二《原理篇》_第2张图片

如果读者看源码如果有困难,可以看一下这个我简化了的JdkDynamicAopProxy类。这个类我保留了主要逻辑,把一些提高代码健壮性的部分去掉了。

JdkDynamicAopProxy 类的主要作用是将切面逻辑织入到目标对象的方法调用中。当使用基于接口的代理方式时,Spring AOP 使用 JDK 动态代理来创建代理对象。JdkDynamicAopProxy 类会根据配置的切面信息,动态地生成一个代理对象,并将切面逻辑织入到该代理对象的方法调用中。

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {

    private final AdvisedSupport advised;

    public JdkDynamicAopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }

    @Override
    public Object getProxy() {
        return Proxy.newProxyInstance(
                getClass().getClassLoader(),
                advised.getTargetSource().getInterfaces(),
                this
        );
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MethodInterceptor methodInterceptor = advised.getMethodInterceptor();
        MethodInvocation methodInvocation = new ReflectiveMethodInvocation(
                advised.getTargetSource().getTarget(),
                method,
                args,
                methodInterceptor,
                advised.getTargetSource().getTargetClass()
        );
        return methodInvocation.proceed();
    }
}

  在该代码中,JdkDynamicAopProxy 类实现了 AopProxyInvocationHandler 接口。

SpringAOP专栏二《原理篇》_第3张图片

AopProxy 接口是 Spring AOP 提供的代理接口,它定义了获取代理对象的方法。JdkDynamicAopProxy 类通过实现该接口,提供了基于 JDK 动态代理的代理对象获取功能。

SpringAOP专栏二《原理篇》_第4张图片

InvocationHandler 接口是 JDK 提供的反射 API 中的一部分,它定义了一个 invoke 方法,用于在代理对象上调用被代理方法。JdkDynamicAopProxy 类实现了 InvocationHandler 接口,通过重写 invoke 方法,实现对被代理方法的增强逻辑。

下面是对简化了的JdkDynamicAopProxy类的详细解读

  1. AdvisedSupport 类型的属性 advised:表示该代理对象所依赖的 AdvisedSupport 对象,该对象包含了切面配置信息。

  2. 构造函数 JdkDynamicAopProxy(AdvisedSupport advised):接收一个 AdvisedSupport 对象作为参数,并将其赋值给 advised 属性。

  3. getProxy() 方法:实现了 AopProxy 接口中的方法,用于获取代理对象。它通过调用 Proxy.newProxyInstance() 方法创建代理对象。

    • getClass().getClassLoader() 获取当前类的类加载器。
    • advised.getTargetSource().getInterfaces() 获取目标对象实现的接口数组。
    • this 表示使用当前对象作为代理对象的 InvocationHandler
  4. invoke(Object proxy, Method method, Object[] args) 方法:实现了 InvocationHandler 接口中的方法,拦截目标对象方法的调用并进行增强逻辑。

    • 首先,从 advised 对象中获取 MethodInterceptor 实例,即切面逻辑。
    • 然后,创建一个 ReflectiveMethodInvocation 实例,传入目标对象、目标方法、方法参数、切面逻辑和目标对象的类信息。
    • 最后,调用 methodInvocation.proceed() 方法执行切面逻辑,并返回方法的执行结果。

CGLIB 代理

        CGLIB 代理是一个基于字节码操作的代理方式,它可以为没有实现接口的类创建代理对象。CGLIB 代理会在运行时生成一个目标对象的子类,并覆盖其中的方法,以实现AOP的功能。

下面是CglibAopProxy类的部分源代码,感兴趣的读者可以自己去看看,我这里就不一一截图给大家看了。

SpringAOP专栏二《原理篇》_第5张图片

SpringAOP专栏二《原理篇》_第6张图片

如果读者看源码如果有困难,可以看一下这个我简化了的CglibAopProxy类。这个类我保留了主要逻辑,对代码进行了简化。

public class CglibAopProxy implements AopProxy {

    private final AdvisedSupport advised;

    public CglibAopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }

    @Override
    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(advised.getTargetSource().getTargetClass());
        enhancer.setCallback(new DynamicAdvisedInterceptor(advised));
        return enhancer.create();
    }

    private static class DynamicAdvisedInterceptor implements MethodInterceptor {

        private final AdvisedSupport advised;

        public DynamicAdvisedInterceptor(AdvisedSupport advised) {
            this.advised = advised;
        }

        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            MethodInvocation methodInvocation = new CglibMethodInvocation(
                    advised.getTargetSource().getTarget(),
                    method,
                    args,
                    proxy,
                    advised.getMethodInterceptor(),
                    advised.getTargetSource().getTargetClass()
            );
            return methodInvocation.proceed();
        }
    }
}

CglibAopProxy 类主要有以下作用

  • 生成代理对象:CglibAopProxy 根据目标对象和切面逻辑生成一个代理对象。与 JDK 动态代理不同,CGLIB 代理不需要目标对象实现接口。

  • 增强方法逻辑:通过继承目标对象,CglibAopProxy 重写目标对象的方法,并在方法中添加切面逻辑。这样,在调用代理对象的方法时,会先执行切面逻辑,然后再调用目标对象的原始方法。

  • 拦截方法调用:CglibAopProxy 使用 MethodInterceptor 接口来定义切面逻辑,并将其应用于生成的代理对象。在代理对象的方法调用过程中,切面逻辑会被触发,从而实现横切关注点的功能,如事务管理、日志记录等。

  • 高性能的代理:相较于 JDK 动态代理,CGLIB 代理使用了字节码生成技术,生成的代理对象是目标对象的子类。这种方式避免了通过反射调用目标对象的方法,提供了更高的执行性能。

下面是CglibAopProxy类的详细解读

  1. CglibAopProxy 类实现了 AopProxy 接口,该接口定义了获取代理对象的方法 getProxy()。SpringAOP专栏二《原理篇》_第7张图片

  2. CglibAopProxy 类的构造方法需要一个 AdvisedSupport 对象作为参数,AdvisedSupport 是 Spring AOP 框架中的核心类,用于保存切面逻辑和目标对象信息。SpringAOP专栏二《原理篇》_第8张图片

  3. getProxy() 方法中,首先实例化了 Enhancer 对象,Enhancer 是 CGLIB 库中的主要类,用于生成代理对象。SpringAOP专栏二《原理篇》_第9张图片

  4. setSuperclass() 方法将目标对象的类设置为要生成的代理类的父类,这样代理类就可以继承目标类的所有非私有方法。

  5. setCallback() 方法用于设置代理类的回调函数,即在代理类的方法调用时,会触发回调函数中的逻辑。

  6. DynamicAdvisedInterceptor 类实现了 MethodInterceptor 接口,这个接口是 CGLIB 库中的接口,用于定义代理类的回调函数。

  7. intercept() 方法是 DynamicAdvisedInterceptor 类的核心方法。当代理类的方法被调用时,intercept() 方法会被触发。在该方法中,首先将目标对象的方法和参数封装成 MethodInvocation 对象,然后调用其 proceed() 方法,从而触发切面逻辑的执行。

  8. CglibMethodInvocation 类是 MethodInvocation 接口的实现类,用于封装目标对象的方法和参数信息。

方法拦截与执行阶段

该阶段的实际执行流程为:

  1. 当代理对象的方法被调用时,会创建一个 ProxyMethodInvocation 对象,并将目标对象、目标方法、方法参数等信息传递给它。

  2. ProxyMethodInvocation 继承了 ReflectiveMethodInvocation 类,因此它可以通过反射调用目标方法。

  3. 在拦截器链的创建过程中,AdvisorChainFactory 会根据切点和通知创建 Advisor 链,即将所有与目标方法匹配的切面的方法拦截器添加到拦截器链中。

  4. 当 ProxyMethodInvocation 执行目标方法时,它会依次遍历拦截器链中的每个方法拦截器。

  5. 每个方法拦截器在方法调用前后执行自己的逻辑,可以实现前置通知、后置通知、异常处理和返回通知等功能。

  6. 方法拦截器的执行顺序与它们在拦截器链中的顺序一致。在方法调用之前,拦截器依次执行前置通知;在方法调用之后,拦截器依次执行后置通知;如果方法发生异常,拦截器执行异常处理逻辑;最后,拦截器执行返回通知。

下面我来分析一下SpringAOP的拦截器

SpringAOP的拦截器

我们先来了解一下拦截器的执行属性:如下图所示

SpringAOP专栏二《原理篇》_第10张图片

接下来看一下AOP拦截器执行原理,拦截器是如何保证不同通知注解下的方法的执行顺序的呢?

在 Spring AOP 中,拦截器链的执行顺序是由 AdvisedSupport 类中的方法获取的。AdvisedSupport 包含了代理对象需要的所有信息,包括目标对象、代理接口、拦截器等。其中,拦截器链的创建和执行是在 ExposeInvocationInterceptor 这个拦截器中完成的。

SpringAOP专栏二《原理篇》_第11张图片

在创建拦截器链时,AdvisorChainFactory 会根据切面的顺序将各个切面的拦截器顺序组合成一个拦截器链。SpringAOP专栏二《原理篇》_第12张图片这样就可以保证 before 在 after 之前执行,因为在创建拦截器链的过程中,会按照切面的顺序将各个切面的拦截器依次添加到链中,最终形成一个有序的拦截器链。

另外,对于同一个方法的多个切面,Spring AOP 会根据切面的顺序将它们的拦截器依次添加到拦截器链中,从而保证了它们的执行顺序。这样就可以保证 find 方法的执行顺序符合切面的定义顺序。

因此,通过 Spring AOP 框架内部对拦截器链的创建和执行机制的设计,可以保证拦截器的执行顺序满足业务需求,确保了 before 在 after 之前执行,并且保证了多个拦截器的执行顺序。

你可能感兴趣的:(java后端,SpringAOP原理,动态代理,字节码操作)