• spring的AOP动态代理--JDK代理和CGLIB代理区分以及注意事项


    大家都知道AOP使用了代理模式,本文主要介绍两个代理模式怎么设置以及区别,对原文一些内容进行了引用后加入了自己的理解和更深入的阐述:
     
    一、JDK代理和CGLIB代理的底层实现区别
    * JDK代理只能针对实现了接口的类以反射的方式生成代理,而不能针对类 ,所以也叫“接口代理”
    * CGLIB是针对类实现代理的,主要对指定的类以字节码转换的方式(ASM框架)生成一个子类,并重写其中的方法。
       因为是创建目标类的子类,所以目标类必须要有无参构造函数(子类的有参构造函数会调用父类无参),否则报错

    【注意】:  有两种特殊情况,static与final方法:
        
     

    Final方法

    Static方法

    Jdk代理

    接口无法使用final关键字,所以不能用。

    【报错】

    接口方法使用了static后代理对象将无法访问此方法,所以不能用。

    【报错】

    Cglib代理

    父类方法使用了final之后,子类将无法对其进行重写,无法拦截。

    【不报错,但不拦截】

    父类方法使用了static之后,子类将无法对其进行重写,无法拦截。

    【不报错,但不拦截】

    同时,当使用cglib代理的时候,目标类一定不能为final类(不能被继承),否则报错。  
     
    以上可以看出使用代理的时候,尽量不要使用final和static关键字
      
     
    二、Spring中两个模式的调配:
    1、如果目标对象实现了接口,默认会采用JDK的动态代理机制实现AOP但是可以强制使用CGLIB实现AOP 
        缺点:必须实现接口,并且生成的代理对象也只能声明成为其中一个接口,其他接口的方法和自己的方法访问不到。
    2、如果目标对象没有实现接口,必须使用CGLIB生成代理,spring会自动在CGLIB和JDK动态代理之间切换 。

    3.如何强制使用CGLIB生成代理? 
    * 添加CGLIB库,<SPRING_HOME>/lib/cglib/*.jar (其实Spring的核心包包括了cglib-nodep-2.2.jar,或者用MyEclipse构建项目也会自动引入
    * 在spring的配置文件的约束中(beans的属性)加入xmlns:aop的相关信息后,再于bean空间中加入: 
    <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
     
    下面是我的applicationContext.xml的前面一部分,以供参考:
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context" 
        xmlns:p="http://www.springframework.org/schema/p"
        xmlns:aop = "http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                            http://www.springframework.org/schema/context 
                            http://www.springframework.org/schema/context/spring-context.xsd
                            http://www.springframework.org/schema/aop 
                            http://www.springframework.org/schema/aop/spring-aop-3.1.xsd "
        >
        
        <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
    ...
    三、注意事项

     当使用jdk代理的时候,经常会发生一个错误:

     java.lang.ClassCastException: com.sun.proxy.$Proxy7 cannot be cast to XXXXX

    解析:明显这是jdk代理对象转换的错误,而从上面可以知道jdk代理是根据目标类的接口生成一个继承这些接口的类对象,当使用容器调出对象的时候就是代理对象与目标对象同级,所以二者之间不能强制转换,声明类应该为他们俩共有的接口类,如果声明成目标类,则会报出以上的错误。

    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");        
    Dao dao = (Dao) ac.getBean("dao"); // 报错

    解决:方法1、将声明类改为目标对象的接口。  IDao dao = (IDao) ac.getBean("dao"); 

       方法2、cglib代理生成的是一个子类,可以用父类进行声明,也不会报错,所以强制使用cglib代理也可以解决。【强制方式上面已经给出】

     
  • 相关阅读:
    java I/O框架 (三)基本流
    java I/O框架 (二)文件操作(File)
    java I/O框架 (一)总览
    8.内部类
    7.权限
    6.继承
    5.代码块
    4.面向对象
    3控制语句
    PHP ksort() 函数
  • 原文地址:https://www.cnblogs.com/Xieyang-blog/p/8976717.html
Copyright © 2020-2023  润新知