Spring AOP 代理机制 JDK&CGLIB

系统 1761 0

Spring AOP使用JDK动态代理或者CGLIB来为目标对象创建代理。(建议优先使用JDK的动态代理)

如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理。所有该目标类型实现的接口都将被代理。 若该目标对象没有实现任何接口,则创建一个CGLIB代理。

如果你希望强制使用CGLIB代理,(例如:希望代理目标对象的所有方法,而不只是实现自接口的方法) 那也可以。但是需要考虑以下问题: 

  • 无法通知(advise)final方法,因为他们不能被覆写。

  • 代理对象的构造器会被调用两次 。因为在CGLIB代理模式下每一个代理对象都会 产生一个子类。每一个代理实例会生成两个对象:实际代理对象和它的一个实现了通知的子类实例 而是用JDK代理时不会出现这样的行为。通常情况下,调用代理类型的构造器两次并不是问题, 因为除了会发生指派外没有任何真正的逻辑被实现。
  • 且CGLib的效率没有使用JDK代理机制高,速度平均要慢8倍左右。

强制使用CGLIB代理需要将<aop:config>的proxy-target-class属性设为true:

1 < aop:config   proxy-target-class = "true" >
2     ...
3 </ aop:config >

当使用@AspectJ自动代理时要强制使用CGLIB,请将<aop:aspectj-autoproxy>的proxy-target-class属性设置为true:

      <aop:aspectj-autoproxy 
      
        proxy-target-class="true"
      
      />
    

Spring AOP是 基于代理机制 的。深刻领会这一句的意思是非常重要的。

考虑如下场景,当你拿到一个无代理的、无任何特殊之处的POJO对象引用时,如以下代码段所示 

 

01 public   class   SimplePojo  implements   Pojo {
02      public   void   foo() {
03         // this next method invocation is a direct call on the 'this' reference
04         this .bar();
05      }
06                   
07      public   void   bar() {
08          // some logic...
09      }
10 }

 

当你调用一个对象引用的方法时,此对象引用上的方法 直接 被调用,如下所示 

Spring AOP 代理机制 JDK&CGLIB

 

1 public   static   void   main(String[] args) {
2     Pojo pojo =  new   SimplePojo();
3     // this is a direct method call on the 'pojo' reference
4     pojo.foo();
5 }

当客户代码所持有的引用是一个代理的时候则略有不同了。请考虑如下图示和代码段片断 

Spring AOP 代理机制 JDK&CGLIB

 

 

1 ProxyFactory proxyFactory =  new   ProxyFactory( new   SimplePojo());
2 proxyFactory.addInterface(Pojo. class );
3 // 添加前置通知
4 proxyFactory.addAdvice( new   BeforeAdviceImpl());
5  
6 Pojo pojo = (Pojo) proxyFactory.getProxy();
7 pojo.foo();

理解此处的关键方法中的客户代码  拥有一个代理的引用 。这意味着对这个对象引用中方法的调用就是对代理的调用, 而这个代理能够代理所有跟特定方法调用相关的拦截器。不过, 一旦调用最终抵达了目标对象  (此处为SimplePojo类的引用), 任何对自身的调用例如this.bar()或者this.foo()将对 this 引用进行调用 而非代理。这一点意义重大, 它意味着自我调用将 会导致和方法调用关联的通知得到执行的机会。

那好,为此要怎么办呢?最好的办法就是重构你的代码使自我调用不会出现。 当然,这的确需要你做一些工作,但却是最好的,最少侵入性的方法。另一个方法则很可怕, 也正因为如此我几乎不愿指出这种方法。你可以象如下这样完全把业务逻辑写在你的Spring AOP类中: 

01 public   class   SimplePojo  implements   Pojo {
02     public   void   foo() {
03        // this works, but... gah!
04        ((Pojo) AopContext.currentProxy()).bar();
05     }
06                   
07     public   void   bar() {
08         // some logic...
09     }
10 }

这样完全将你的代码交给了Spring AOP,  并且 让类本身知道它正被用于一个AOP的上下文中, 而它其中的文件直接面对AOP。当代理在被创建时也需要一些额外的配置:

 

 

01 public   static   void   main(String[] args) {
02      ProxyFactory factory =  new   ProxyFactory( new   SimplePojo());
03      factory.adddInterface(Pojo. class );
04      factory.addAdvice( new   RetryAdvice());
05      factory.setExposeProxy( true );
06                   
07      Pojo pojo = (Pojo) factory.getProxy();
08                   
09      // this is a method call on the proxy!
10      pojo.foo();
11 }

上面的例子中用到了Spring AOP中ProxyFactory这些特定的API。在使用Spring容器配置的环境下也同样有此问题,同样以之前的支付为例:

 

 

01 public   interface   IPayService {
02      String pay( long   userId,  long   money);
03      String inner();
04 }
05 // 实现类,在pay方法中调用了inner()方法
06 @Service
07 public   class   RMBPayService  implements   IPayService {
08      private   static   final   Logger LOGGER = LoggerFactory.getLogger(RMBPayService. class );
09  
10      @Override
11      public   String pay( long   userId,  long   money) {
12          LOGGER.info( "用户:"   + userId +  "使用人民币支付金额:"   + money);
13          inner();
14          return   "ok" ;
15      }
16  
17      @Override
18      public   String inner() {
19          LOGGER.info( "inner method,can you see the aop advice...." );
20          return   "go" ;
21      }
22 }

这样从容器中获取RMBPayService实例对象时,调用pay()方法,则只能在pay()方法环境下看到aop的特性,而在inner()中则看不到,可以使用如下方法来解决(虽然不建议):

 

 

01 @Service
02 public   class   RMBPayService  implements   IPayService {
03      private   static   final   Logger LOGGER = LoggerFactory.getLogger(RMBPayService. class );
04  
05      @Override
06      public   String pay( long   userId,  long   money) {
07          LOGGER.info( "用户:"   + userId +  "使用人民币支付金额:"   + money);
08  
09          ApplicationContext ctx =  new   ClassPathXmlApplicationContext( "applicationContext.xml" );
10  
11          IPayService service = ctx.getBean(RMBPayService. class );
12          service.inner();
13          return   "ok" ;
14      }
15  
16      @Override
17      public   String inner() {
18  
19          LOGGER.info( "inner method,can you see the aop advice...." );
20          return   "go" ;
21      }
22 }

Spring AOP 代理机制 JDK&CGLIB


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论