Spring AOP 将 Advice(通知)织入到目标对象的过程,主要依赖于动态代理技术(JDK 动态代理或 CGLIB 动态代理)。下面分别介绍这两种代理方式下,Spring 如何织入 Advice:
1. JDK 动态代理:
-
条件: 目标对象必须实现至少一个接口。
-
原理:
- Spring 使用
java.lang.reflect.Proxy
类创建代理对象。 - 代理对象实现了与目标对象相同的接口。
- 代理对象持有一个
java.lang.reflect.InvocationHandler
接口的实现类(通常是 Spring 提供的JdkDynamicAopProxy
)的引用。 - 当客户端调用代理对象的方法时,实际上会调用
InvocationHandler
的invoke()
方法。 invoke()
方法负责:- 根据配置的通知类型(前置通知、后置通知、环绕通知等),在调用目标方法前后执行相应的通知逻辑。
- 通过反射调用目标对象的真实方法。
- Spring 使用
-
织入过程:
- 创建
InvocationHandler
: Spring 根据切面(Aspect)和通知(Advice)配置,创建一个InvocationHandler
的实现类(JdkDynamicAopProxy
)。 - 创建代理对象: Spring 使用
Proxy.newProxyInstance()
方法创建代理对象,传入类加载器、目标对象实现的接口数组、以及InvocationHandler
实例。 - 方法调用: 当客户端调用代理对象的方法时,会触发
InvocationHandler
的invoke()
方法。 - 执行通知:
invoke()
方法根据配置的通知类型,在调用目标方法前后执行相应的通知逻辑。- 前置通知(Before Advice): 在调用目标方法之前执行。
- 后置通知(After Advice): 在调用目标方法之后执行(无论是否发生异常)。
- 返回通知(After Returning Advice): 在目标方法成功返回后执行。
- 异常通知(After Throwing Advice): 在目标方法抛出异常后执行。
- 环绕通知(Around Advice): 包裹目标方法,可以在目标方法执行前后执行自定义逻辑,甚至可以阻止目标方法的执行。
- 调用目标方法:
invoke()
方法通过反射调用目标对象的真实方法。 - 返回结果: 将目标方法的返回值(如果有)返回给客户端。
- 创建
2. CGLIB 动态代理:
-
条件: 目标对象不需要实现接口。
-
原理:
- Spring 使用 CGLIB 库创建代理对象。
- 代理对象是目标对象的子类。
- 代理对象重写了目标对象的所有非 final 方法。
- 在重写的方法中,Spring 会根据配置的通知类型,在调用父类(即目标对象)的方法前后,插入相应的通知逻辑。
- CGLIB 使用 ASM 字节码操作框架来生成子类。
-
织入过程:
- 创建 MethodInterceptor: Spring根据切面和通知, 会创建一个
MethodInterceptor
。 - 创建代理对象: Spring 使用 CGLIB 的
Enhancer
类创建代理对象,设置父类为目标类,设置 Callback 为MethodInterceptor
。 - 方法调用: 当客户端调用代理对象的方法时,会触发
MethodInterceptor
的intercept
方法。 - 执行通知:
intercept
方法根据配置的通知类型,在调用目标方法前后执行相应的通知逻辑。- 通知类型与 JDK 动态代理相同(前置、后置、返回、异常、环绕)。
- 调用目标方法: 通过调用父类(即目标对象)的相应方法。
- 返回结果: 将目标方法的返回值(如果有)返回给客户端。
- 创建 MethodInterceptor: Spring根据切面和通知, 会创建一个
总结:
Spring AOP 通过动态代理技术将 Advice 织入到目标对象中。
- JDK 动态代理: 基于接口,通过
InvocationHandler
在目标方法调用前后执行通知。 - CGLIB 动态代理: 基于类,通过生成目标类的子类,在子类方法中调用父类方法前后执行通知。
无论是 JDK 动态代理还是 CGLIB 动态代理,Spring AOP 的核心思想都是:在不修改目标对象源代码的情况下,通过代理对象来增强目标对象的功能,实现横切关注点的模块化。