zoukankan      html  css  js  c++  java
  • JDK和Cglib动态代理

    一:动态代理的引入

       Spring中的两大核心之一的 AOP是基于 动态代理实现的,简单来说就是面向切面编程.Spring AOP的实现对于接口来说就是使用的JDK的动态代理来实现的,而对于类的代理使用CGLIB来实现。

    二动态代理的概念

        代理类在程序运行时创建的代理方式被成为 动态代理.也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。代理模式最大的特点就是代理类和实际业务类实现同一个接口(或继承同一父类),代理对象持有一个实际对象的引用,外部调用时操作的是代理对象,而在代理对象的内部实现中又会去调用实际对象的操作,Java动态代理其实内部也是通过Java反射机制来实现的,即已知的一个对象,然后在运行时动态调用其方法,这样在调用前后作一些相应的处理。

    三:JDK动态代理的原理

      JDK的动态代理,就是在程序运行的过程中,根据被代理的接口来动态生成代理类的class文件,并加载运行的过程。代理的目的是调用目标方法时可以转而执行InvocationHandler拦截器invoke方法以及类的反射,实际上spring aop也是在这里做文章。这也是典型的代理模式 。

    四:JDK动态代理的实现

      1>首先有一个实现了InvocationHandler接口的类

    package com.svse.daili.jdk_cglib;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    /**
    *JDK的动态代理对象
    *前提:被代理的类实现了接口才可以进行代理
    *原理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用invokeHandler的invoke()方法来处理
    *InvocationHandler其实底层为一个拦截器
    */
    public class JDKProxy implements InvocationHandler
    {
        private Object targetObject;//需要代理的目标对象

       public JDKProxy(Object targetObject){
       this.targetObject=targetObject;
       }

       public Object proxyObject()
      {
        // 三个参数 (被加载的类、代理的接口、代理的目标对象)
       // newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
       return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
       targetObject.getClass().getInterfaces(), this);//返回代理对象
       }

       @Override
       public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
       {
       checkPopedom();
       Object rs=null;
       rs=method.invoke(targetObject, args);
      return rs;
      }

       private void checkPopedom(){
       System.out.println("...模拟检测权限 checkPopedom()");
        }
      }

       2>定义一个接口

      package com.svse.daili.jdk_cglib;
      public interface UserManager
      {
      public void addUser(String id,String password);
      public void delUser(String id);
      }

       3>定义一个实现该接口的类

      package com.svse.daili.jdk_cglib;
      public class UserManagerImpl implements UserManager
      {

        @Override
        public void addUser(String id, String password)
        {
           System.out.println("...调用了UserManagerImpl的addUser()方法");
        }

        @Override
        public void delUser(String id)
       {
         System.out.println("...调用了UserManagerImpl的delUser()方法");

        }
      }

      4>测试类

    package com.svse.daili.jdk_cglib;
    /**
    * 测试代理对象
    * @author Administrator
    *
    */
    public class JDKProxyTest
    {
       public static void main(String[] args)
       {
          JDKProxy jdkProxy=new JDKProxy(new UserManagerImpl());
          UserManager proxyManager=(UserManager) jdkProxy.proxyObject();//返回代理对象
          System.out.println("-----------------JDK动态代理----------------");
          proxyManager.addUser("", "");
         proxyManager.delUser("");
        }
    }

      5>测试结果

    6>疑问?为什么jdk动态代理必须基于接口   原因如下: 

       1)、生成的代理类继承了Proxy,由于java是单继承,所以只能实现接口,通过接口实现 
       2)、从代理模式的设计来说,充分利用了java的多态特性,也符合基于接口编码的规范 
      当然,jdk在生成代理的参数中也说明了,需要传入对应接口。

    五:Cglib动态代理的原理

          利用字节码处理框架ASM开源包,主要是对指定的类生成一个子类,覆盖其中的所有方法,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。该类或方法不能声明为final。

    六:Cglib动态代理的实现

      1>创建一个类并实现MethodInterceptor接口

    package com.svse.daili.cglib;
    import java.lang.reflect.Method;
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    /**
    * cglib动态代理 (需要引入asm.jar 和cglib.jar)
    * @author Administrator
    *
    */
    public class CglibProxy implements MethodInterceptor
    {
         private Object targetObject;

         public CglibProxy(Object obj){
         this.targetObject=obj;
        }

        public Object createProxyObject(){
          Enhancer enhancer=new Enhancer();
          enhancer.setSuperclass(targetObject.getClass());
          enhancer.setCallback(this);
          Object proxyObj=enhancer.create();
          return proxyObj;//返回代理对象
        }

       public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable
       {
        if("sayHello".equals(method.getName())){
           checkPopedom();
        }
         Object obj=null;
         obj=method.invoke(targetObject, args);
         return obj;
        }

         private void checkPopedom(){
         System.out.println("...模拟检测权限 checkPopedom()");
       }
     }

      2>创建一个代理类

    package com.svse.daili.cglib;

    public class StudentManager {
    public void sayHello(){
         System.out.println("...调用了StudentManager类的sayHello()方法");
      }
    }

    3>测试类

    package com.svse.daili.cglib;
    public class CglixProxyTest
    {

        //过程中遇到的问题:如果cglib.jar 版本为3.0以上,可能会报类找不到,原因跟asm.jar版本冲突
        public static void main(String[] args) {
            CglibProxy cglibProxy=new CglibProxy(new StudentManager());
            StudentManager studentProxy=(StudentManager) cglibProxy.createProxyObject();
            System.out.println("-----------------Cglix动态代理----------------");
            studentProxy.sayHello();
          }
      }

    4>测试结果

      

    七:jdk代理与cglib代理的区别

       (1)JDK动态代理只能对实现了接口的类生成代理,而不能针对普通类

        (2)CGLIB是针对普通类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类

          (3)JDK代理是不需要以来第三方的库,只需要JDK环境就可以进行代理,CGLib 必须依赖于CGLib的类库(asm和cglib)

          (4)JDK代理类必须实现InvocationHandler接口 ,通过Proxy.newProxyInstance产生代理对象;而CGLIB是实现MethodInterceptor接口,通过cglibProxy.createProxyObject()产生代理对象

          (5)在AOP中,如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP,也可以强制使用CGLIB实现AOP;如果目标对象没有实现了接口,必须采用CGLIB库进行代理,spring会自动在JDK动态代理和CGLIB之间转换

    八:二者优缺点分析

       (1) 使用JDK动态代理,目标类必须实现的某个接口,如果某个类没有实现接口则不能生成代理对象。

       (2)CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。因为是覆盖其中的所有方法,所以目标类和方法不能声明为final类型。

        (3)从执行效率上看,Cglib动态代理效率较高。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

        (4) CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。同时,由于CGLib是采用动态创建子类的方法,对于final方法,无法进行代理

  • 相关阅读:
    linux中nc命令
    Centos6.5 安装zabbix3(收藏,非原创)
    紀念
    算法学习资源收集
    一道奇怪的求和题
    P5717 题解
    P1424 刷题记录
    P1888 题解
    P1422 刷题记录
    P1055 题解
  • 原文地址:https://www.cnblogs.com/zhaosq/p/9876779.html
Copyright © 2011-2022 走看看