jdk 和 cglib 在 Spring 中的统一

Spring AOP抽象

Spring 中对切点、通知、切面的抽象如下
  • 切点:接口 Pointcut,典型实现 AspectJExpressionPointcut
    • notion image
  • 通知:典型接口为 MethodInterceptor 代表环绕通知
    • notion image
  • 切面:Advisor,包含一个 Advice 通知,PointcutAdvisor 包含一个 Advice 通知和一个 Pointcut
classDiagram class Advice class MethodInterceptor class Advisor class PointcutAdvisor Pointcut <|-- AspectJExpressionPointcut Advice <|-- MethodInterceptor Advisor <|-- PointcutAdvisor PointcutAdvisor o-- "一" Pointcut PointcutAdvisor o-- "一" Advice <<interface>> Advice <<interface>> MethodInterceptor <<interface>> Pointcut <<interface>> Advisor <<interface>> PointcutAdvisor
 

Spring AOP cglib和jdk代理的选择

代理相关类图
  • AopProxyFactory 根据 proxyTargetClass 等设置选择 AopProxy 实现
  • AopProxy 通过 getProxy 创建代理对象
  • 图中 Proxy 都实现了 Advised 接口,能够获得关联的切面集合与目标(其实是从 ProxyFactory 取得)
  • 调用代理方法时,会借助 ProxyFactory 将通知统一转为环绕通知:MethodInterceptor
classDiagram Advised <|-- ProxyFactory ProxyFactory o-- Target ProxyFactory o-- "多" Advisor ProxyFactory --> AopProxyFactory : 使用 AopProxyFactory --> AopProxy Advised <|-- 基于CGLIB的Proxy 基于CGLIB的Proxy <-- ObjenesisCglibAopProxy : 创建 AopProxy <|-- ObjenesisCglibAopProxy AopProxy <|-- JdkDynamicAopProxy 基于JDK的Proxy <-- JdkDynamicAopProxy : 创建 Advised <|-- 基于JDK的Proxy class AopProxy { +getProxy() Object } class ProxyFactory { proxyTargetClass : boolean } class ObjenesisCglibAopProxy { advised : ProxyFactory } class JdkDynamicAopProxy { advised : ProxyFactory } <<interface>> Advised <<interface>> AopProxyFactory <<interface>> AopProxy

proxyTargetClass = false, 目标实现了接口, 用 jdk 实现

public class A15 { public static void main(String[] args) { /* 两个切面概念 aspect = 通知1(advice) + 切点1(pointcut) 通知2(advice) + 切点2(pointcut) 通知3(advice) + 切点3(pointcut) ... advisor = 更细粒度的切面,包含一个通知和切点 */ // 1. 备好切点 AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression("execution(* foo())"); // 2. 备好通知 MethodInterceptor advice = invocation -> { System.out.println("before..."); Object result = invocation.proceed(); // 调用目标 System.out.println("after..."); return result; }; // 3. 备好切面 DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice); /* 4. 创建代理 a. proxyTargetClass = false, 目标实现了接口, 用 jdk 实现 b. proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现 c. proxyTargetClass = true, 总是使用 cglib 实现 */ Target2 target = new Target2(); ProxyFactory factory = new ProxyFactory(); factory.setTarget(target); factory.addAdvisor(advisor); factory.setInterfaces(target.getClass().getInterfaces()); factory.setProxyTargetClass(false); Target2 proxy = (Target2) factory.getProxy(); System.out.println(proxy.getClass()); proxy.foo(); proxy.bar(); /* 学到了什么 a. Spring 的代理选择规则 b. 底层的切点实现 c. 底层的通知实现 d. ProxyFactory 是用来创建代理的核心实现, 用 AopProxyFactory 选择具体代理实现 - JdkDynamicAopProxy - ObjenesisCglibAopProxy */ } interface I1 { void foo(); void bar(); } static class Target1 implements I1 { public void foo() { System.out.println("target1 foo"); } public void bar() { System.out.println("target1 bar"); } } static class Target2 { public void foo() { System.out.println("target2 foo"); } public void bar() { System.out.println("target2 bar"); } } }
notion image

proxyTargetClass = true, 总是使用 cglib 实现

Target1 target = new Target1(); ProxyFactory factory = new ProxyFactory(); factory.setTarget(target); factory.addAdvisor(advisor); factory.setInterfaces(target.getClass().getInterfaces()); factory.setProxyTargetClass(true); I1 proxy = (I1) factory.getProxy(); System.out.println(proxy.getClass()); proxy.foo(); proxy.bar();
 
class com.onethink.a15.A15$Target1$$EnhancerBySpringCGLIB$$9fe985e9 before... target1 foo after... target1 bar

proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现

使用没有实现接口的Target2
Target2 target = new Target2(); ProxyFactory factory = new ProxyFactory(); factory.setTarget(target); factory.addAdvisor(advisor); factory.setInterfaces(target.getClass().getInterfaces()); factory.setProxyTargetClass(false); Target2 proxy = (Target2) factory.getProxy(); System.out.println(proxy.getClass()); proxy.foo(); proxy.bar();
class com.onethink.a15.A15$Target2$$EnhancerBySpringCGLIB$$e581ba21 before... target2 foo after... target2 bar