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动态代理

  • 相关阅读:
    【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目(8) 权限管理,自定义权限,扩展权限
    致博客园网友
    【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目(7.2) 模块管理,模块的添加、修改、删除
    【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目(7.1) 模块管理,验证权限,展示模块列表
    【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目(6) 控制器基类 主要做登录用户、权限认证、日志记录等工作
    【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目(5.5) 登录功能的实现,完善登录功能
    【C#公共帮助类】 WebHelper帮助类
    【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目(5.4) 登录功能的实现,创建与登录用户相关的接口和实现类
    【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目(5.3) 登录功能的实现,丰富数据表、建立关联
    【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目(5.2) 登录功能的实现,接口注入、log4net的使用
  • 原文地址:https://www.cnblogs.com/itzhoucong/p/12143704.html
Copyright © 2011-2022 走看看