一个关于Spring AOP很有意思的问题。

Java 代码

  1. <br>public class UserDAOImpl{  
  2. <br><br>    public void save() {  
  3. <br>        // TODO Auto-generated method stub  
  4. <br>        System.out.println("user saved");  
  5. <br>    }  
  6. <br>}  
  7. <br>// 相关配置,省略了一些不相关内容  
  8. <br><bean id="userDAO" class="UserDAOImpl">  
  9. <br><bean id="userDAOProxy"  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">   
  10. <br>    <property name="target">   
  11. <br>        <ref local="userDAO" />   
  12. <br>    </property>   
  13. <br></bean>  


public class UserDAOImpl{



public void save() {


  // TODO Auto-generated method stub


  System.out.println("user saved");


}


}


//
相关配置,省略了一些不相关内容


<bean id="userDAO" class="UserDAOImpl">


<bean id="userDAOProxy"  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">


    <property name="target">


        <ref local="userDAO" />


    </property>


</bean>

   测试代码

Java 代码

  1.         ApplicationContext ctx =  
  2. <br>            new FileSystemXmlApplicationContext("applicationContext.xml");  
  3. <br>        UserDAOImpl userDAOImpl =   
  4. <br>            (UserDAOImpl)ctx.getBean("userDAOProxy");  
  5. <br>        userDAOImpl.save();  

  ApplicationContext ctx =


   new FileSystemXmlApplicationContext("applicationContext.xml");


  UserDAOImpl userDAOImpl =


   (UserDAOImpl)ctx.getBean("userDAOProxy");


  userDAOImpl.save();

   上面这种情况下程序可以正常运行,但是如果UserDAOImpl 实现了一个接口,其他不变

Java 代码

  1. public class UserDAOImpl implements UserDAO {  
  2. <br>  
  3. <br>    public void save() {  
  4. <br>        // TODO Auto-generated method stub  
  5. <br>        System.out.println("user saved");  
  6. <br>    }  
  7. <br>  
  8. <br>}  

public class UserDAOImpl implements UserDAO {




public void save() {


  // TODO Auto-generated method stub


  System.out.println("user saved");


}




}

  这种情况下,程序将不能正常运行,会抛出java.lang.ClassCastException 异常

理解上面这种情况产生的原因需要了解Spring AOP 的实现原理。
Spring
实现AOP 是依赖JDK 动态代理和CGLIB 代理实现的。
以下是JDK 动态代理和CGLIB 代理简单介绍
    JDK
动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。
    CGLIB
代理:实现原理类似于JDK 动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB 是高效的代码生成包,底层是依靠ASM (开源的java 字节码编辑类库)操作字节码实现的,性能比JDK 强。
Spring
是依靠什么来判断采用哪种代理策略来生成AOP 代理呢?以下代码就是Spring 的判断逻辑

Java 代码

  1.     //org.springframework.aop.framework.DefaultAopProxyFactory  
  2. <br>    // 参数AdvisedSupport Spring AOP 配置相关类  
  3. <br>    public AopProxy createAopProxy(AdvisedSupport advisedSupport)  
  4. <br>            throws AopConfigException {  
  5. <br>        // 在此判断使用JDK 动态代理还是CGLIB 代理  
  6. <br>        if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass()  
  7. <br>                || hasNoUserSuppliedProxyInterfaces(advisedSupport)) {  
  8. <br>            if (!cglibAvailable) {  
  9. <br>                throw new AopConfigException(  
  10. <br>                        "Cannot proxy target class because CGLIB2 is not available. "  
  11. <br>                                + "Add CGLIB to the class path or specify proxy interfaces.");  
  12. <br>            }  
  13. <br>            return CglibProxyFactory.createCglibProxy(advisedSupport);  
  14. <br>        } else {  
  15. <br>            return new JdkDynamicAopProxy(advisedSupport);  
  16. <br>        }  
  17. <br>    }  

    //org.springframework.aop.framework.DefaultAopProxyFactory


//
参数AdvisedSupport Spring AOP 配置相关类


public AopProxy createAopProxy(AdvisedSupport advisedSupport)


   throws AopConfigException {


  //
在此判断使用JDK 动态代理还是CGLIB 代理


  if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass()


    || hasNoUserSuppliedProxyInterfaces(advisedSupport)) {


   if (!cglibAvailable) {


    throw new AopConfigException(


      "Cannot proxy target class because CGLIB2 is not available. "


        + "Add CGLIB to the class path or specify proxy interfaces.");


   }


   return CglibProxyFactory.createCglibProxy(advisedSupport);


  } else {


   return new JdkDynamicAopProxy(advisedSupport);


  }


}

 

         advisedSupport.isOptimize() advisedSupport.isProxyTargetClass() 默认返回都是false ,所以在默认情况下目标对象有没有实现接口决定着Spring 采取的策略,当然可以设置advisedSupport.isOptimize() 或者advisedSupport.isProxyTargetClass() 返回为true ,这样无论目标对象有没有实现接口Spring 都会选择使用CGLIB 代理。所以在默认情况下,如果一个目标对象如果实现了接口Spring 则会选择JDK 动态代理策略动态的创建一个接口实现类(动态代理类)来代理目标对象,可以通俗的理解这个动态代理类是目标对象的另外一个版本,所以这两者之间在强制转换的时候会抛出j ava.lang.ClassCastException 。而所以在默认情况下,如果目标对象没有实现任何接口,Spring 会选择CGLIB 代理, 其生成的动态代理对象是目标类的子类。

 

 

   

     以上说的是默认情况下,也可以手动配置一些选项使 Spring 采用 CGLIB 代理。

org.springframework.transaction.interceptor.TransactionProxyFactoryBean org.springframework.aop.framework. ProxyConfig 的子类,所以可以参照 ProxyConfig 里的一些设置如下所示,将 optimize proxyTargetClass 任意一个设置为 true 都可以强制 Spring 采用 CGLIB 代理。

Java 代码

  1. // 相关配置,省略了一些不相关内容  
  2. <br><bean id="userDAO" class="UserDAOImpl">  
  3. <br><bean id="userDAOProxy"  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">   
  4. <br>    <property name="target">   
  5. <br>        <ref local="userDAO" />   
  6. <br>    </property>   
  7. <br>    <property name="optimize">   
  8. <br>        <value>true</value>  
  9. <br>    </property>  
  10. <br>    <property name="proxyTargetClass">   
  11. <br>        <value>true</value>  
  12. <br>    </property>   
  13. <br></bean>  

// 相关配置,省略了一些不相关内容


<bean id="userDAO" class="UserDAOImpl">


<bean id="userDAOProxy"  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">


    <property name="target">


        <ref local="userDAO" />


    </property>


    <property name="optimize">


        <value>true</value>


    </property>


    <property name="proxyTargetClass">


        <value>true</value>


    </property>


</bean>

 

使用 CGLIB 代理也就不会出现前面提到的ClassCastException 问题了,

也可以在性能上有所提高,但是也有它的弊端, Spring doc 原文解释如下 optimization will usually mean that advice changes won't  take effect after a proxy has been created. For this reason, optimization  is disabled by default

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值