zoukankan      html  css  js  c++  java
  • JDK动态代理案例与原理分析

    一、JDK动态代理实现案例

    Person接口

    package com.zhoucong.proxy.jdk;
    
    public interface Person {
    
    //    寻找真爱
        void findlove();
        
    }

    人物实现类

    package com.zhoucong.proxy.jdk;
    
    public class Zhangsan implements Person{
        
        @Override
        public void findlove() {
            System.out.println("我叫张三,性别女,我找对象的要求如下:
    ");
            System.out.println("高富帅");
            System.out.println("有房有车");
            System.out.println("身高180cm以上,体重70kg");
        }
    
    }

    代理类

    package com.zhoucong.proxy.jdk;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class MeiPo implements InvocationHandler{
    
        private Person target;
        
    //  获取被代理人的个人资料
        public Object getInstance(Person target) throws Exception {
            this.target = target;
            Class clazz = target.getClass();
            System.out.println("被代理对象的class是:"+ clazz);
            return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
            
        }
        
        
     @Override
        public Object invoke(Object arg0, Method method, Object[] args) throws Throwable {
         System.out.println("我是媒婆!!");
         System.out.println("开始信息海选...");
         System.out.println("-------------");
         
         method.invoke(this.target, args);
         System.out.println("-------------");
         System.out.println("如果合适的话,就准备办事");
         return null;
        }
    
    }

    运行测试

    package com.zhoucong.proxy.jdk;
    public class TestFindLove {
    
        public static void main(String[] args) {
    
            try {
                Person obj = (Person) new MeiPo().getInstance(new Zhangsan());
                System.out.println(obj.getClass());
                obj.findlove();
    
                /**
                 * 原理: 
                 * 1.拿到被代理对象的引用,然后获取它的接口
                 * 2.jdk代理重新生成一个类,同时实现我们个额的代理对象所实现的接口
                 * 3.把被代理对象的引用也拿到了
                 * 4.重新动态生成一个class字节码
                 * 5.然后编译
                 */
    
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    }

    运行结果:

    重点:

    代理前对象为  com.zhoucong.proxy.jdk.Zhangsan

    代理后获得的对象  com.sun.proxy.$Proxy0

    二、原理分析

    获取$Proxy0.class字节码内容

    package com.zhoucong.proxy.jdk;
    
    import java.io.FileOutputStream;
    import sun.misc.ProxyGenerator;
    
    
    public class TestFindLove {
    
        public static void main(String[] args) {
    
            try {
                Person obj = (Person) new MeiPo().getInstance(new Zhangsan());
                System.out.println(obj.getClass());
                obj.findlove();
    
                
    //            获取字节码内容
    //            ProxyGenerator需要导入jdk安装目录jre/bin下的rt.jar
    
                byte[] generateProxyClass = ProxyGenerator.generateProxyClass("$Proxy0", new Class[] { Person.class });
    //            输出到本地
                FileOutputStream os = new FileOutputStream("E:/GP_WORKSPACE/$Proxy0.class");
                os.write(generateProxyClass);
                os.close();
    
        
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    }

    利用反编译工具得到Java文件如下:

    $Proxy0.java

    import com.zhoucong.proxy.jdk.Person;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class $Proxy0 extends Proxy implements Person {
      private static Method m1;
      
      private static Method m2;
      
      private static Method m0;
      
      private static Method m3;
      
      public $Proxy0(InvocationHandler paramInvocationHandler) {
        super(paramInvocationHandler);
      }
      
      public final boolean equals(Object paramObject) {
        try {
          return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
        } catch (Error|RuntimeException error) {
          throw null;
        } catch (Throwable throwable) {
          throw new UndeclaredThrowableException(throwable);
        } 
      }
      
      public final String toString() {
        try {
          return (String)this.h.invoke(this, m2, null);
        } catch (Error|RuntimeException error) {
          throw null;
        } catch (Throwable throwable) {
          throw new UndeclaredThrowableException(throwable);
        } 
      }
      
      public final int hashCode() {
        try {
          return ((Integer)this.h.invoke(this, m0, null)).intValue();
        } catch (Error|RuntimeException error) {
          throw null;
        } catch (Throwable throwable) {
          throw new UndeclaredThrowableException(throwable);
        } 
      }
      
      public final void findlove() {
        try {
          this.h.invoke(this, m3, null);
          return;
        } catch (Error|RuntimeException error) {
          throw null;
        } catch (Throwable throwable) {
          throw new UndeclaredThrowableException(throwable);
        } 
      }
      
      static {
        try {
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
          m3 = Class.forName("com.zhoucong.proxy.jdk.Person").getMethod("findlove", new Class[0]);
          return;
        } catch (NoSuchMethodException noSuchMethodException) {
          throw new NoSuchMethodError(noSuchMethodException.getMessage());
        } catch (ClassNotFoundException classNotFoundException) {
          throw new NoClassDefFoundError(classNotFoundException.getMessage());
        } 
      }
    }

    要点分析:

    特点:1、JDK代理生成的类实现了Person接口继承父类Proxy

        分析:JDK实现动态代理必须要有接口(cglib没有)

       2、静态代码块获取了接口中的方法

    m3 = Class.forName("com.zhoucong.proxy.jdk.Person").getMethod("findlove", new Class[0]);

       3、JDK代理生成的类实现了接口方法,里面写了重要的一句话

     this.h.invoke(this, m3, null);

    分析:this.h 去父类Proxy中查看得知为父类的成员变量

    /**
         * the invocation handler for this proxy instance.
         * @serial
         */
        protected InvocationHandler h;

    即MeiPo类并且调用了里面的Object invoke(Object arg0, Method method, Object[] args)方法

     @Override
        public Object invoke(Object arg0, Method method, Object[] args) throws Throwable {
         System.out.println("我是媒婆!!");
         System.out.println("开始信息海选...");
         System.out.println("-------------");
        // this.target.findlove();  //还可以写成如下,效果一样
      // 利用反射机制实现
         method.invoke(this.target, args);
         System.out.println("-------------");
         System.out.println("如果合适的话,就准备办事");
         return null;
        }

    三、手写实现JDK动态代理

  • 相关阅读:
    共享纸巾更换主板代码分析 共享纸巾主板更换后的对接代码
    Python Django Ajax 传递列表数据
    Python Django migrate 报错解决办法
    Python 创建字典的多种方式
    Python 两个list合并成一个字典
    Python 正则 re.sub替换
    python Django Ajax基础
    Python Django 获取表单数据的三种方式
    python Django html 模板循环条件
    Python Django ORM 字段类型、参数、外键操作
  • 原文地址:https://www.cnblogs.com/itzhoucong/p/12143704.html
Copyright © 2011-2022 走看看