Spring AOP 代理机制 JDK&CGLIB

标签: spring aop 代理 | 发表时间:2013-12-26 20:18 | 作者:xiaoshanjnby
出处:http://www.iteye.com

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 }

 

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

 

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 }

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

 

 

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 }


已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [spring aop 代理] 推荐:

Spring AOP 代理机制 JDK&CGLIB

- - 开源软件 - ITeye博客
Spring AOP使用JDK动态代理或者CGLIB来为目标对象创建代理. (建议优先使用JDK的动态代理). 如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理. 所有该目标类型实现的接口都将被代理. 若该目标对象没有实现任何接口,则创建一个CGLIB代理. 如果你希望强制使用CGLIB代理,(例如:希望代理目标对象的所有方法,而不只是实现自接口的方法) 那也可以.

Spring AOP详解

- - Java - 编程语言 - ITeye博客
        最近项目中遇到了以下几点需求,仔细思考之后,觉得采用AOP来解决. 一方面是为了以更加灵活的方式来解决问题,另一方面是借此机会深入学习Spring AOP相关的内容. 例如,以下需求不用AOP肯定也能解决,至于是否牵强附会,仁者见仁智者见智. 1.对部分函数的调用进行日志记录,用于观察特定问题在运行过程中的函数调用情况.

Spring AOP动态代理原理与实现方式 (转)

- - 开源软件 - ITeye博客
AOP:面向切面、面向方面、面向接口是一种横切技术. 1.事务管理: (1)数据库事务:(2)编程事务(3)声明事物:Spring AOP-->声明事物   . 3.安全验证: Spring AOP---OOP升级  . 静态代理原理:目标对象:调用业务逻辑    代理对象:日志管理. 表示层调用--->代理对象(日志管理)-->调用目标对象.

Spring AOP监控SQL执行

- - CSDN博客架构设计推荐文章
         对数据库连接池Proxool比较熟悉的读者,都知道Proxool可以记录SQL执行内容和时间等信息日志. 我们可以将该日志记录专门的SQL日志文件,对于查找执行特别耗时的SQL起了不小的作用. 对于一些其他连接池,没有该特性时,本文介绍Spring AOP切面方法来记录SQL日志.

Spring AOP 实现原理与 CGLIB 应用

- - 博客 - 伯乐在线
来源: IBM Developerworks. 简介: AOP(Aspect Orient Programming),也就是面向方面编程,作为面向对象编程的一种补充,专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在 Java EE 应用中,常常通过 AOP 来处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等.

使用spring AOP获得session的思路

- - RSS - IT博客云
由于Spring 的AOP面向切面编程,与Servlet容器没有任何关联,所以想要获得Session会话比较麻烦. 当然Struts2同样不依赖Servlet容器,可以在Spring AOP中可以使用 com.opensymphony.xwork2.ActionContext,就可以获得 Session.

Spring AOP + Redis缓存数据库查询

- - 编程语言 - ITeye博客
我们希望能够将数据库查询结果缓存到Redis中,这样在第二次做同样的查询时便可以直接从redis取结果,从而减少数据库读写次数. 必须要做到与业务逻辑代码完全分离. 从缓存中读出的数据必须与数据库中的数据一致. 如何为一个数据库查询结果生成一个唯一的标识. Key),能唯一确定一个查询结果,同一个查询结果,一定能映射到同一个.

AOP代理失效的两种情况

- - Shaojunying's Blog
被代理对象的private方法不能被代理. cdlib技术使用子类来代理父类,子类看不到父类的private,也就无法代理private方法. JDK动态代理是基于接口来实现的,而接口不允许有private方法,也就不会有出现这个问题. 如果方法a()调用处于同一个类内的方法b(),那么相当于this.b(),这里的this代表没有被代理的方法.

基于 Annotation 拦截的 Spring AOP 权限验证方法

- - 企业架构 - ITeye博客
转自: http://www.ibm.com/developerworks/cn/java/j-lo-springaopfilter/index.html. 使用 Annotation 可以非常方便的根据用户的不同角色,分配访问 Java 方法的权限. 在 Java Web 开发中,使用这种方法,可以提高系统的松耦合度,方便维护.

Spring aop 原理及各种应用场景

- - 开源软件 - ITeye博客
AOP是Aspect Oriented Programing的简称,面向切面编程. AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理、缓存、对象池管理以及日志记录. AOP将这些分散在各个业务逻辑中的代码通过横向切割的方式抽取到一个独立的模块中. AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类,其中静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;而动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强.