java动态代理

原文链接: https://my.oschina.net/u/3755458/blog/1628190

原文链接:http://blog.csdn.net/u011784767/article/details/78281384

说起java动态代理,在我刚开始学java时对这项技术也是十分困惑,明明可以直接调通的对象方法为什么还要使用动态代理?随着学习的不断深入和工作经验的积累,慢慢的体会并理解了java动态代理机制。昨天再给公司新同事做技术培训时有同学就对动态代理产生了疑问,我这里梳理一遍一并记录一下,方便大家查看对自己也是加深记忆。

(1)什么是代理?

大道理上讲代理是一种软件设计模式,目的地希望能做到代码重用。具体上讲,代理这种设计模式是通过不直接访问被代理对象的方式,而访问被代理对象的方法。这个就好比 商户---->明星经纪人(代理)---->明星这种模式。我们可以不通过直接与明星对话的情况下,而通过明星经纪人(代理)与其产生间接对话。

 

(2)什么情况下使用代理?

(1)设计模式中有一个设计原则是开闭原则,是说对修改关闭对扩展开放,我们在工作中有时会接手很多前人的代码,里面代码逻辑让人摸不着头脑(sometimes the code is really like shit),这时就很难去下手修改代码,那么这时我们就可以通过代理对类进行增强。

(2)我们在使用RPC框架的时候,框架本身并不能提前知道各个业务方要调用哪些接口的哪些方法 。那么这个时候,就可用通过动态代理的方式来建立一个中间人给客户端使用,也方便框架进行搭建逻辑,某种程度上也是客户端代码和框架松耦合的一种表现。

(3)Spring的AOP机制就是采用动态代理的机制来实现切面编程。

 

(3)静态代理和动态代理

我们根据加载被代理类的时机不同,将代理分为静态代理和动态代理。如果我们在代码编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理;如果不能确定,那么可以使用类的动态加载机制,在代码运行期间加载被代理的类这就是动态代理,比如RPC框架和Spring AOP机制。

 

(4)静态代理

我们先创建一个接口,遗憾的是java api代理机制求被代理类必须要实现某个接口,对于静态代理方式代理类也要实现和被代理类相同的接口;对于动态代理代理类则不需要显示的实现被代理类所实现的接口。

 

[java] view plain copy

  1. /** 
  2.  * 顶层接口 
  3.  * @author yujie.wang 
  4.  * 
  5.  */  
  6. public interface Person {  
  7.     public void sayHello(String content, int age);  
  8.     public void sayGoodBye(boolean seeAgin, double time);  
  9. }  

 

[java] view plain copy

  1. /** 
  2.  * 需要被代理的类 实现了一个接口Person 
  3.  * @author yujie.wang 
  4.  * 
  5.  */  
  6. public class Student implements Person{  
  7.   
  8.     @Override  
  9.     public void sayHello(String content, int age) {  
  10.         // TODO Auto-generated method stub  
  11.         System.out.println("student say hello" + content + " "+ age);  
  12.     }  
  13.   
  14.     @Override  
  15.     public void sayGoodBye(boolean seeAgin, double time) {  
  16.         // TODO Auto-generated method stub  
  17.         System.out.println("student sayGoodBye " + time + " "+ seeAgin);  
  18.     }  
  19.   
  20. }  

 

[java] view plain copy

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


测试代码输出:

 

[java] view plain copy

  1. ProxyTest sayHello begin  
  2. student say hellowelcome to java 20  
  3. ProxyTest sayHello end  
  4. ******  
  5. ProxyTest sayHello begin  
  6. student sayGoodBye 100.0 true  
  7. ProxyTest sayHello end  


静态代理看起来是比较简单的,没有什么问题只不过是在代理类中引入了被代理类的对象而已。

那么接下来我们看看动态代理。

(5)动态代理

我们先直接上动态代理的代码,之后再分析代码的行为,上面的Person接口和Student被代理类保持不变。

 

[java] view plain copy

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

运行测试代码输出如下结果:

 

[java] view plain copy

  1. MyInvocationHandler invoke begin  
  2. proxy: com.sun.proxy.$Proxy0  
  3. method: sayHello  
  4. arg: yujie.wang  
  5. arg: 20  
  6. student say helloyujie.wang 20  
  7. MyInvocationHandler invoke end  
  8. MyInvocationHandler invoke begin  
  9. proxy: com.sun.proxy.$Proxy0  
  10. method: sayGoodBye  
  11. arg: true  
  12. arg: 100.0  
  13. student sayGoodBye 100.0 true  
  14. MyInvocationHandler invoke end  
  15. end  



仔细分析上面的动态代理实现代码,我们看到这里涉及到java反射包下的一个接口InvocationHandler和一个类Proxy。

 

[java] view plain copy

  1. package java.lang.reflect;  
  2. public interface InvocationHandler {  
  3.     public Object invoke(Object proxy, Method method, Object[] args)  
  4.     throws Throwable;  
  5. }  

这个接口只有一个invoke方法,我们在通过代理类调用被代理类的方法时,最终都会委托给这个invoke方法执行,

[java] view plain copy

  1. //通过代理类调用 被代理类的方法  
  2. proxy.sayHello("yujie.wang", 20);  
  3. proxy.sayGoodBye(true, 100);  


所以我们就可以在这个invoke方法中对被代理类进行增强或做一些其他操作。

Proxy类的public static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)方法内部通过拼接字节码的方式来创建代理类,后面我会反编译出它所创建的代理类看看内容。

我们看这个方法的三个参数:

ClassLoader loader:指定一个动态加载代理类的类加载器

Class[] interfaces:指明被代理类实现的接口,之后我们通过拼接字节码生成的类才能知道调用哪些方法。

InvocationHandler h:这是一个方法委托类,我们通过代理调用被代理类的方法时,就可以将方法名和方法参数都委托给这个委托类。

你看我们现在有了类加载器、类实现的接口、要调用方法的方法名和参数,那么我们就可以做很多事情了。

(6)反编译Proxy.newProxyInstance所创建的代理类

//这一句是生成代理类的class文件,前提是你需要在工程根目录下创建com/sun/proxy目录,不然会报找不到路径的io异常
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

我们在代码中加入上述代码,代码就会保存生成的代理类,名称为$Proxy0.class

java动态代理_第1张图片

通过jd-gui反编译代码如下,其中注释是我加上去的:

 

[java] view plain copy

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


总结一下:

jdk的代理让我们在不直接访问某些对象的情况下,通过代理机制也可以访问被代理对象的方法,这种技术可以应用在很多地方比如RPC框架,Spring AOP机制,但是我们看到jdk的代理机制必须要求被代理类实现某个方法,这样在生成代理类的时候才能知道重新那些方法。这样一个没有实现任何接口的类就无法通过jdk的代理机制进行代理,当然解决方法是使用cglib的代理机制进行代理。

转载于:https://my.oschina.net/u/3755458/blog/1628190

你可能感兴趣的:(java动态代理)