spring 动态代理

Spring的aop(面向切面编程)是通过代理实现,Spring的代理分为2种。

  • JDK dynamic proxies(JDK)
  • CGLIB

JDK

默认情况下,当一个类实现了接口,Spring 就会使用JDK代理,但是只代理接口的方法。如果一个对象有其它非接口方法,非接口方法是不会被代理的。

CGLIB

CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类。
CGLIB代理会创建2个对象,一个是被代理对象,一个是实现了advice(增强逻辑,或切面逻辑)的sub class。所以CGLIB不能代理final修饰的class。

容易混淆的代理语义

public class SimplePojo implements Pojo {
   public void foo() {
      // this next method invocation is a direct call on the 'this' reference
      this.bar();
   }
      public void bar() {
      // some logic...
   }
}
foo方法直接调用
image.png
public class Main {
   public static void main(String[] args) {
         Pojo pojo = new SimplePojo();
            // this is a direct method call on the 'pojo' reference
      pojo.foo();
   }
}
foo方法代理调用
image.png
public class Main {
   public static void main(String[] args) {
         ProxyFactory factory = new ProxyFactory(new SimplePojo());
      factory.addInterface(Pojo.class);
      factory.addAdvice(new RetryAdvice());
      Pojo pojo = (Pojo) factory.getProxy();
            // this is a method call on the proxy!
      pojo.foo();
   }
}

foo方法是被代理的,但是foo方法调用bar方法时用的是this,所以bar方法不会被代理。

bar方法的代理调用
public class SimplePojo implements Pojo {
   public void foo() {
      // this works, but... gah!
      ((Pojo) AopContext.currentProxy()).bar();
   }
      public void bar() {
      // some logic...
   }
}

((Pojo) AopContext.currentProxy()).bar(); 是先获取代理对象,再调用bar方法。
ps:这里只是为了方便阐述代理才使用 ((Pojo) AopContext.currentProxy());
((Pojo) AopContext.currentProxy()); 会是代码跟spring 强耦合,不建议这样使用。

bar方法的代理调用方案思考

目前主要有几种方式

  • 将foo方法和bar方法独立成2个对象。
  • 将SimplePojo 对象再注入到 SimplePojo
SimplePojo {
   SimplePojo  self;
}

判断是否被代理,区分是JDK代理还是CGLIB代理

//org.springframework.aop.support.AopUtils
AopUtils.isAopProxy
AopUtils.isJdkDynamicProxy
AopUtils.isCglibProxy

AspectJ

AspectJ 没有上面的 self-invocation 问题,因为AspectJ不是一个proxy的aop框架。
Aspectj原理:通过asm操作Java字节码的方式。

参考:

https://docs.spring.io/spring-framework/docs/3.0.0.M3/reference/html/ch08s06.html

你可能感兴趣的:(spring 动态代理)