• java之静态代理与动态代理


    先看看静态代理是如何操作的

    定义接口:

    1 public interface Person {
    2     public void sayHello(String content, int age);
    3     public void sayGoodBye(boolean seeAgin, double time);
    4 }

    实际的类:

     1 public class Student implements Person{
     2  
     3     @Override
     4     public void sayHello(String content, int age) {
     5         // TODO Auto-generated method stub
     6         System.out.println("student say hello" + content + " "+ age);
     7     }
     8  
     9     @Override
    10     public void sayGoodBye(boolean seeAgin, double time) {
    11         // TODO Auto-generated method stub
    12         System.out.println("student sayGoodBye " + time + " "+ seeAgin);
    13     }
    14 }

    代理类:

     1 public class ProxyTest implements Person{
     2     
     3     private Person o;
     4     
     5     public ProxyTest(Person o){
     6         this.o = o;
     7     }
     8  
     9     @Override
    10     public void sayHello(String content, int age) {
    11         // TODO Auto-generated method stub
    12         System.out.println("ProxyTest sayHello begin");
    13         //在代理类的方法中 间接访问被代理对象的方法
    14         o.sayHello(content, age);
    15         System.out.println("ProxyTest sayHello end");
    16     }
    17  
    18     @Override
    19     public void sayGoodBye(boolean seeAgin, double time) {
    20         // TODO Auto-generated method stub
    21         System.out.println("ProxyTest sayHello begin");
    22         //在代理类的方法中 间接访问被代理对象的方法
    23         o.sayGoodBye(seeAgin, time);
    24         System.out.println("ProxyTest sayHello end");
    25     }
    26 
    27 public static void main(String[] args) {
    28         // TODO Auto-generated method stub
    29         //s为被代理的对象,某些情况下 我们不希望修改已有的代码,我们采用代理来间接访问
    30         Student s = new Student();
    31         //创建代理类对象
    32         ProxyTest proxy = new ProxyTest(s);
    33         //调用代理类对象的方法
    34         proxy.sayHello("welcome to java", 20);
    35         System.out.println("******");
    36         //调用代理类对象的方法
    37         proxy.sayGoodBye(true, 100);
    38  
    39     }
    40  
    41 }

    可以看到,静态代理类要求实现与实际类型相同的接口,这个虽然在某些情况下有使用场景,但是其实扩展起来很麻烦,需要一个个的进行重载,相比之下,动态代理就好多了。

    动态代理也需要一个代理类,实现特定的接口InvocationHandler:

     1 public class MyInvocationHandler implements InvocationHandler{
     2     
     3     private Object object;
     4     
     5     public MyInvocationHandler(Object object){
     6         this.object = object;
     7     }
     8  
     9     @Override
    10     public Object invoke(Object proxy, Method method, Object[] args)
    11             throws Throwable {
    12         // TODO Auto-generated method stub
    13         System.out.println("MyInvocationHandler invoke begin");
    14         System.out.println("proxy: "+ proxy.getClass().getName());
    15         System.out.println("method: "+ method.getName());
    16         for(Object o : args){
    17             System.out.println("arg: "+ o);
    18         }
    19         //通过反射调用 被代理类的方法
    20         method.invoke(object, args);
    21         System.out.println("MyInvocationHandler invoke end");
    22         return null;
    23     }
    24     
    25     public static void main(String [] args){
    26         //创建需要被代理的类
    27         Student s = new Student();
    28         //这一句是生成代理类的class文件,前提是你需要在工程根目录下创建com/sun/proxy目录,不然会报找不到路径的io异常
    29         System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
    30         //获得加载被代理类的 类加载器
    31         ClassLoader loader = Thread.currentThread().getContextClassLoader();
    32         //指明被代理类实现的接口
    33         Class<?>[] interfaces = s.getClass().getInterfaces();
    34         // 创建被代理类的委托类,之后想要调用被代理类的方法时,都会委托给这个类的invoke(Object proxy, Method method, Object[] args)方法
    35         MyInvocationHandler h = new MyInvocationHandler(s);
    36         //生成代理类
    37         Person proxy = (Person)Proxy.newProxyInstance(loader, interfaces, h);
    38         //通过代理类调用 被代理类的方法
    39         proxy.sayHello("yujie.wang", 20);
    40         proxy.sayGoodBye(true, 100);
    41         System.out.println("end");
    42     }
    43  
    44 }

    这个灵活性就好多了,不用关心自己代理的类到底有多少方法,只用实现一个invoke方法,去做自己想做的事情就好,如果需要根据特定的方法做事情,可能就需要根据method判断了,不如静态代理写在重载方法中感觉好。

    那么,java到底是怎么实现动态代理的,不妨反编译一下,拿到的结果如下,可以看到其实是jvm帮你做了静态代理的事情:

      1 public final class $Proxy0 extends Proxy implements Person{
      2   private static Method m4;
      3   private static Method m1;
      4   private static Method m0;
      5   private static Method m3;
      6   private static Method m2;
      7   
      8   public $Proxy0(InvocationHandler paramInvocationHandler)
      9     throws 
     10   {
     11     super(paramInvocationHandler);
     12   }
     13    //实现了Person接口的方法,这就是我们调用这个方法Proxy.newProxyInstance必须提供第二个参数的作用 
     14   public final void sayGoodBye(boolean paramBoolean, double paramDouble)
     15     throws 
     16   {
     17     try
     18     {
     19     // 我们看到通过调用代理类的方法时,最终方法都会委托给InvocationHandler实现类的invoke方法
     20     // m4为代理类通过反射获得的Method
     21       this.h.invoke(this, m4, new Object[] { Boolean.valueOf(paramBoolean), Double.valueOf(paramDouble) });
     22       return;
     23     }
     24     catch (Error|RuntimeException localError)
     25     {
     26       throw localError;
     27     }
     28     catch (Throwable localThrowable)
     29     {
     30       throw new UndeclaredThrowableException(localThrowable);
     31     }
     32   }
     33   
     34   public final boolean equals(Object paramObject)
     35     throws 
     36   {
     37     try
     38     {
     39       return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
     40     }
     41     catch (Error|RuntimeException localError)
     42     {
     43       throw localError;
     44     }
     45     catch (Throwable localThrowable)
     46     {
     47       throw new UndeclaredThrowableException(localThrowable);
     48     }
     49   }
     50   
     51   public final int hashCode()
     52     throws 
     53   {
     54     try
     55     {
     56       return ((Integer)this.h.invoke(this, m0, null)).intValue();
     57     }
     58     catch (Error|RuntimeException localError)
     59     {
     60       throw localError;
     61     }
     62     catch (Throwable localThrowable)
     63     {
     64       throw new UndeclaredThrowableException(localThrowable);
     65     }
     66   }
     67   //实现了Person接口的方法,这就是我们调用这个方法Proxy.newProxyInstance必须提供第二个参数的作用 
     68   public final void sayHello(String paramString, int paramInt)
     69     throws 
     70   {
     71     try
     72     {
     73       // 我们看到通过调用代理类的方法时,最终方法都会委托给InvocationHandler实现类的invoke方法
     74       // m4为代理类通过反射获得的Method
     75       this.h.invoke(this, m3, new Object[] { paramString, Integer.valueOf(paramInt) });
     76       return;
     77     }
     78     catch (Error|RuntimeException localError)
     79     {
     80       throw localError;
     81     }
     82     catch (Throwable localThrowable)
     83     {
     84       throw new UndeclaredThrowableException(localThrowable);
     85     }
     86   }
     87   
     88   public final String toString()
     89     throws 
     90   {
     91     try
     92     {
     93       return (String)this.h.invoke(this, m2, null);
     94     }
     95     catch (Error|RuntimeException localError)
     96     {
     97       throw localError;
     98     }
     99     catch (Throwable localThrowable)
    100     {
    101       throw new UndeclaredThrowableException(localThrowable);
    102     }
    103   }
    104   
    105   static
    106   {
    107     try
    108     {//代理类通过反射 获得的接口方法Method
    109       m4 = Class.forName("com.yujie.proxy.dynamic.Person").getMethod("sayGoodBye", new Class[] { Boolean.TYPE, Double.TYPE });
    110       m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
    111       m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
    112       //代理类通过反射 获得的接口方法Method
    113       m3 = Class.forName("com.yujie.proxy.dynamic.Person").getMethod("sayHello", new Class[] { Class.forName("java.lang.String"), Integer.TYPE });
    114       m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
    115       return;
    116     }
    117     catch (NoSuchMethodException localNoSuchMethodException)
    118     {
    119       throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    120     }
    121     catch (ClassNotFoundException localClassNotFoundException)
    122     {
    123       throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    124     }
    125   }
    126 }

    本文参考了https://blog.csdn.net/u011784767/article/details/78281384

  • 相关阅读:
    175. 组合两个表
    101. 对称二叉树
    292. Nim游戏
    319. 灯泡开关
    155. 最小栈
    232. 用栈实现队列
    225. 用队列实现栈
    145. 二叉树的后序遍历
    144. 二叉树的前序遍历
    【leetcode】977. Squares of a Sorted Array
  • 原文地址:https://www.cnblogs.com/029zz010buct/p/10497605.html
Copyright © 2020-2023  润新知