上一篇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示例化的流程图
切面织入阶段
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类的部分源码,感兴趣的读者可以自己去看看,我这里就不一一截图给大家看了。
如果读者看源码如果有困难,可以看一下这个我简化了的JdkDynamicAopProxy类。这个类我保留了主要逻辑,把一些提高代码健壮性的部分去掉了。
JdkDynamicAopProxy 类的主要作用是将切面逻辑织入到目标对象的方法调用中。当使用基于接口的代理方式时,Spring AOP 使用 JDK 动态代理来创建代理对象。JdkDynamicAopProxy 类会根据配置的切面信息,动态地生成一个代理对象,并将切面逻辑织入到该代理对象的方法调用中。
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {private final AdvisedSupport advised;public JdkDynamicAopProxy(AdvisedSupport advised) {this.advised = advised;}@Overridepublic Object getProxy() {return Proxy.newProxyInstance(getClass().getClassLoader(),advised.getTargetSource().getInterfaces(),this);}@Overridepublic 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 类实现了 AopProxy 和 InvocationHandler 接口。
AopProxy 接口是 Spring AOP 提供的代理接口,它定义了获取代理对象的方法。JdkDynamicAopProxy 类通过实现该接口,提供了基于 JDK 动态代理的代理对象获取功能。
InvocationHandler 接口是 JDK 提供的反射 API 中的一部分,它定义了一个 invoke 方法,用于在代理对象上调用被代理方法。JdkDynamicAopProxy 类实现了 InvocationHandler 接口,通过重写 invoke 方法,实现对被代理方法的增强逻辑。
下面是对简化了的JdkDynamicAopProxy类的详细解读
AdvisedSupport
类型的属性advised
:表示该代理对象所依赖的AdvisedSupport
对象,该对象包含了切面配置信息。构造函数
JdkDynamicAopProxy(AdvisedSupport advised)
:接收一个AdvisedSupport
对象作为参数,并将其赋值给advised
属性。
getProxy()
方法:实现了AopProxy
接口中的方法,用于获取代理对象。它通过调用Proxy.newProxyInstance()
方法创建代理对象。
getClass().getClassLoader()
获取当前类的类加载器。advised.getTargetSource().getInterfaces()
获取目标对象实现的接口数组。this
表示使用当前对象作为代理对象的InvocationHandler
。
invoke(Object proxy, Method method, Object[] args)
方法:实现了InvocationHandler
接口中的方法,拦截目标对象方法的调用并进行增强逻辑。
- 首先,从
advised
对象中获取MethodInterceptor
实例,即切面逻辑。- 然后,创建一个
ReflectiveMethodInvocation
实例,传入目标对象、目标方法、方法参数、切面逻辑和目标对象的类信息。- 最后,调用
methodInvocation.proceed()
方法执行切面逻辑,并返回方法的执行结果。
CGLIB 代理
CGLIB 代理是一个基于字节码操作的代理方式,它可以为没有实现接口的类创建代理对象。CGLIB 代理会在运行时生成一个目标对象的子类,并覆盖其中的方法,以实现AOP的功能。
下面是CglibAopProxy类的部分源代码,感兴趣的读者可以自己去看看,我这里就不一一截图给大家看了。
如果读者看源码如果有困难,可以看一下这个我简化了的CglibAopProxy类。这个类我保留了主要逻辑,对代码进行了简化。
public class CglibAopProxy implements AopProxy {private final AdvisedSupport advised;public CglibAopProxy(AdvisedSupport advised) {this.advised = advised;}@Overridepublic 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;}@Overridepublic 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类的详细解读
CglibAopProxy 类实现了 AopProxy 接口,该接口定义了获取代理对象的方法 getProxy()。
CglibAopProxy 类的构造方法需要一个 AdvisedSupport 对象作为参数,AdvisedSupport 是 Spring AOP 框架中的核心类,用于保存切面逻辑和目标对象信息。
getProxy() 方法中,首先实例化了 Enhancer 对象,Enhancer 是 CGLIB 库中的主要类,用于生成代理对象。
setSuperclass() 方法将目标对象的类设置为要生成的代理类的父类,这样代理类就可以继承目标类的所有非私有方法。
setCallback() 方法用于设置代理类的回调函数,即在代理类的方法调用时,会触发回调函数中的逻辑。
DynamicAdvisedInterceptor 类实现了 MethodInterceptor 接口,这个接口是 CGLIB 库中的接口,用于定义代理类的回调函数。
intercept() 方法是 DynamicAdvisedInterceptor 类的核心方法。当代理类的方法被调用时,intercept() 方法会被触发。在该方法中,首先将目标对象的方法和参数封装成 MethodInvocation 对象,然后调用其 proceed() 方法,从而触发切面逻辑的执行。
CglibMethodInvocation 类是 MethodInvocation 接口的实现类,用于封装目标对象的方法和参数信息。
方法拦截与执行阶段
该阶段的实际执行流程为:
当代理对象的方法被调用时,会创建一个 ProxyMethodInvocation 对象,并将目标对象、目标方法、方法参数等信息传递给它。
ProxyMethodInvocation 继承了 ReflectiveMethodInvocation 类,因此它可以通过反射调用目标方法。
在拦截器链的创建过程中,AdvisorChainFactory 会根据切点和通知创建 Advisor 链,即将所有与目标方法匹配的切面的方法拦截器添加到拦截器链中。
当 ProxyMethodInvocation 执行目标方法时,它会依次遍历拦截器链中的每个方法拦截器。
每个方法拦截器在方法调用前后执行自己的逻辑,可以实现前置通知、后置通知、异常处理和返回通知等功能。
方法拦截器的执行顺序与它们在拦截器链中的顺序一致。在方法调用之前,拦截器依次执行前置通知;在方法调用之后,拦截器依次执行后置通知;如果方法发生异常,拦截器执行异常处理逻辑;最后,拦截器执行返回通知。
下面我来分析一下SpringAOP的拦截器
SpringAOP的拦截器
我们先来了解一下拦截器的执行属性:如下图所示
接下来看一下AOP拦截器执行原理,拦截器是如何保证不同通知注解下的方法的执行顺序的呢?
在 Spring AOP 中,拦截器链的执行顺序是由 AdvisedSupport 类中的方法获取的。AdvisedSupport 包含了代理对象需要的所有信息,包括目标对象、代理接口、拦截器等。其中,拦截器链的创建和执行是在 ExposeInvocationInterceptor 这个拦截器中完成的。
在创建拦截器链时,AdvisorChainFactory 会根据切面的顺序将各个切面的拦截器顺序组合成一个拦截器链。这样就可以保证 before 在 after 之前执行,因为在创建拦截器链的过程中,会按照切面的顺序将各个切面的拦截器依次添加到链中,最终形成一个有序的拦截器链。
另外,对于同一个方法的多个切面,Spring AOP 会根据切面的顺序将它们的拦截器依次添加到拦截器链中,从而保证了它们的执行顺序。这样就可以保证 find 方法的执行顺序符合切面的定义顺序。
因此,通过 Spring AOP 框架内部对拦截器链的创建和执行机制的设计,可以保证拦截器的执行顺序满足业务需求,确保了 before 在 after 之前执行,并且保证了多个拦截器的执行顺序。