JDK 动态代理

JAVA动态代理内部实现

一 代理设计模式

代理模式为目标对象提供一种代理以控制对实际对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

为了保持行为的一致性,代理类和实际委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。

 代理模式类图  

常见的代理有:
1) 远程代理(Remote proxy):对一个位于不同的地址空间对象提供一个局域代表对象,如RMI中的stub。 
2) 虚拟代理(Virtual proxy):根据需要将一个资源消耗很大或者比较复杂的对象,延迟加载,在真正需要的时候才创建。
3) 保护代理(Protect or Access Proxy):控制对一个对象的访问权限。
4) 智能引用(Smart Reference Proxy):提供比目标对象额外的服务和功能。

通过代理类这一中间层,能够有效控制对实际委托类对象的直接访问,也可以很好地隐藏和保护实际对,实施不同的控制策略,从而在设计上获得了更大的灵活性。

二 动态代理使用

JAVA动态代理机制以巧妙的方式实现了代理模式的设计理念。

 

 

动态代理类图


动态代理在代理ProxySubject和RealSubject之间增加了InvocationHandler,这是一种通信间接化, 增加了灵 性性,例如可以把这个中间层实现为一个框架Framework,直接通过xml文件等方式来调用RealSubject。

在普通的设计中,我们一般不会使用动态代理。但是在一些框架结构设计中,动态代理非常重要,如RMI,EJB中都使用动态代理。

 

  1. interface Subject  
  2. {  
  3.   public void doSomething();  
  4. }  
  5. class RealSubject implements Subject  
  6. {  
  7.   public void doSomething()  
  8.   {  
  9.     System.out.println( "call doSomething()" );  
  10.   }  
  11. }  
  12. class ProxyHandler implements InvocationHandler  
  13. {  
  14.   private Object proxied;  
  15.     
  16.   public ProxyHandler( Object proxied )  
  17.   {  
  18.     this.proxied = proxied;  
  19.   }  
  20.     
  21.   public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable  
  22.   {  
  23.     return method.invoke( proxied, args);  
  24.   }   
  25. }  

 

  1. import java.lang.reflect.InvocationHandler;  
  2. import java.lang.reflect.Method;  
  3. import java.lang.reflect.Proxy;  
  4. import sun.misc.ProxyGenerator;  
  5. import java.io.*;  
  6. public class DynamicProxy  
  7. {  
  8.   public static void main( String args[] )  
  9.   {  
  10.     RealSubject real = new RealSubject();  
  11.     Subject proxySubject = ( Subject )     
  12.            Proxy.newProxyInstance(   
  13.            Subject.class.getClassLoader(),   
  14.                  new Class[] { Subject.class },   
  15.                    new ProxyHandler( real ) );  
  16.       
  17.     proxySubject.doSomething();  
  18.   //write proxySubject class binary data to file  
  19.     createProxyClassFile();  
  20.   }  
  21.     
  22.   public static void createProxyClassFile()  
  23.   {  
  24.     String name = "ProxySubject";  
  25.     byte[] data = ProxyGenerator.generateProxyClass( name, new Class[] { Subject.class } );  
  26.     try  
  27.     {  
  28.       FileOutputStream out = new FileOutputStream( name + ".class" );  
  29.       out.write( data );  
  30.       out.close();  
  31.     }  
  32.     catch( Exception e )  
  33.     {  
  34.       e.printStackTrace();  
  35.     }  
  36.   }  
  37. }  

 

三 动态代理内部实现

类Proxy的getProxyClass方法调用ProxyGenerator的 generateProxyClass方法产生ProxySubject.class的二进制数据:


public static byte[] generateProxyClass(final String name, Class[] interfaces)

 

我们可以import sun.misc.ProxyGenerator,调用 generateProxyClass方法产生binary data,然后写入文件,最后通过反编译工具来查看内部实现原理。

 

反编译后的ProxySubject.java:

  1. import java.lang.reflect.*;  
  2. public final class ProxySubject extends Proxy  
  3.     implements Subject  
  4. {  
  5.     private static Method m1;  
  6.     private static Method m0;  
  7.     private static Method m3;  
  8.     private static Method m2;  
  9.     public ProxySubject(InvocationHandler invocationhandler)  
  10.     {  
  11.         super(invocationhandler);  
  12.     }  
  13.     public final boolean equals(Object obj)  
  14.     {  
  15.         try  
  16.         {  
  17.             return ((Boolean)super.h.invoke(this, m1, new Object[] {  
  18.                 obj  
  19.             })).booleanValue();  
  20.         }  
  21.         catch(Error _ex) { }  
  22.         catch(Throwable throwable)  
  23.         {  
  24.             throw new UndeclaredThrowableException(throwable);  
  25.         }  
  26.     }  
  27.     public final int hashCode()  
  28.     {  
  29.         try  
  30.         {  
  31.             return ((Integer)super.h.invoke(this, m0, null)).intValue();  
  32.         }  
  33.         catch(Error _ex) { }  
  34.         catch(Throwable throwable)  
  35.         {  
  36.             throw new UndeclaredThrowableException(throwable);  
  37.         }  
  38.     }  
  39.     public final void doSomething()  
  40.     {  
  41.         try  
  42.         {  
  43.             super.h.invoke(this, m3, null);  
  44.             return;  
  45.         }  
  46.         catch(Error _ex) { }  
  47.         catch(Throwable throwable)  
  48.         {  
  49.             throw new UndeclaredThrowableException(throwable);  
  50.         }  
  51.     }  
  52.     public final String toString()  
  53.     {  
  54.         try  
  55.         {  
  56.             return (String)super.h.invoke(this, m2, null);  
  57.         }  
  58.         catch(Error _ex) { }  
  59.         catch(Throwable throwable)  
  60.         {  
  61.             throw new UndeclaredThrowableException(throwable);  
  62.         }  
  63.     }  
  64.     static   
  65.     {  
  66.         try  
  67.         {  
  68.             m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {  
  69.                 Class.forName("java.lang.Object")  
  70.             });  
  71.             m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);  
  72.             m3 = Class.forName("Subject").getMethod("doSomething", new Class[0]);  
  73.             m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);  
  74.         }  
  75.         catch(NoSuchMethodException nosuchmethodexception)  
  76.         {  
  77.             throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
  78.         }  
  79.         catch(ClassNotFoundException classnotfoundexception)  
  80.         {  
  81.             throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
  82.         }  
  83.     }  
  84. }  

 

通过 ProxySubject.java,我们可以看到动态代理的内部是如何实现的,并且我们可以实现自己的一个动态代理生成器。

ProxyGenerator内部是如何生成class二进制数据,可以参考源代码。

 

  1. private byte[] generateClassFile() {  
  2.   /* 
  3.    * Record that proxy methods are needed for the hashCode, equals, 
  4.    * and toString methods of java.lang.Object.  This is done before 
  5.    * the methods from the proxy interfaces so that the methods from 
  6.    * java.lang.Object take precedence over duplicate methods in the 
  7.    * proxy interfaces. 
  8.    */  
  9.   addProxyMethod(hashCodeMethod, Object.class);  
  10.   addProxyMethod(equalsMethod, Object.class);  
  11.   addProxyMethod(toStringMethod, Object.class);  
  12.   /* 
  13.    * Now record all of the methods from the proxy interfaces, giving 
  14.    * earlier interfaces precedence over later ones with duplicate 
  15.    * methods. 
  16.    */  
  17.   for (int i = 0; i < interfaces.length; i++) {  
  18.       Method[] methods = interfaces[i].getMethods();  
  19.       for (int j = 0; j < methods.length; j++) {  
  20.     addProxyMethod(methods[j], interfaces[i]);  
  21.       }  
  22.   }  
  23.   /* 
  24.    * For each set of proxy methods with the same signature, 
  25.    * verify that the methods' return types are compatible. 
  26.    */  
  27.   for (List<ProxyMethod> sigmethods : proxyMethods.values()) {  
  28.       checkReturnTypes(sigmethods);  
  29.   }  
  30.   /* ============================================================ 
  31.    * Step 2: Assemble FieldInfo and MethodInfo structs for all of 
  32.    * fields and methods in the class we are generating. 
  33.    */  
  34.   try {  
  35.       methods.add(generateConstructor());  
  36.       for (List<ProxyMethod> sigmethods : proxyMethods.values()) {  
  37.     for (ProxyMethod pm : sigmethods) {  
  38.         // add static field for method's Method object  
  39.         fields.add(new FieldInfo(pm.methodFieldName,  
  40.       "Ljava/lang/reflect/Method;",  
  41.        ACC_PRIVATE | ACC_STATIC));  
  42.         // generate code for proxy method and add it  
  43.         methods.add(pm.generateMethod());  
  44.     }  
  45.       }  
  46.       methods.add(generateStaticInitializer());  
  47.   } catch (IOException e) {  
  48.       throw new InternalError("unexpected I/O Exception");  
  49.   }  
  50.   /* ============================================================ 
  51.    * Step 3: Write the final class file. 
  52.    */  
  53.   /* 
  54.    * Make sure that constant pool indexes are reserved for the 
  55.    * following items before starting to write the final class file. 
  56.    */  
  57.   cp.getClass(dotToSlash(className));  
  58.   cp.getClass(superclassName);  
  59.   for (int i = 0; i < interfaces.length; i++) {  
  60.       cp.getClass(dotToSlash(interfaces[i].getName()));  
  61.   }  
  62.   /* 
  63.    * Disallow new constant pool additions beyond this point, since 
  64.    * we are about to write the final constant pool table. 
  65.    */  
  66.   cp.setReadOnly();  
  67.   ByteArrayOutputStream bout = new ByteArrayOutputStream();  
  68.   DataOutputStream dout = new DataOutputStream(bout);  
  69.   try {  
  70.       /* 
  71.        * Write all the items of the "ClassFile" structure. 
  72.        * See JVMS section 4.1. 
  73.        */  
  74.           // u4 magic;  
  75.       dout.writeInt(0xCAFEBABE);  
  76.           // u2 minor_version;  
  77.       dout.writeShort(CLASSFILE_MINOR_VERSION);  
  78.           // u2 major_version;  
  79.       dout.writeShort(CLASSFILE_MAJOR_VERSION);  
  80.       cp.write(dout);   // (write constant pool)  
  81.           // u2 access_flags;  
  82.       dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);  
  83.           // u2 this_class;  
  84.       dout.writeShort(cp.getClass(dotToSlash(className)));  
  85.           // u2 super_class;  
  86.       dout.writeShort(cp.getClass(superclassName));  
  87.           // u2 interfaces_count;  
  88.       dout.writeShort(interfaces.length);  
  89.           // u2 interfaces[interfaces_count];  
  90.       for (int i = 0; i < interfaces.length; i++) {  
  91.     dout.writeShort(cp.getClass(  
  92.         dotToSlash(interfaces[i].getName())));  
  93.       }  
  94.           // u2 fields_count;  
  95.       dout.writeShort(fields.size());  
  96.           // field_info fields[fields_count];  
  97.       for (FieldInfo f : fields) {  
  98.     f.write(dout);  
  99.       }  
  100.           // u2 methods_count;  
  101.       dout.writeShort(methods.size());  
  102.           // method_info methods[methods_count];  
  103.       for (MethodInfo m : methods) {  
  104.     m.write(dout);  
  105.       }  
  106.              // u2 attributes_count;  
  107.       dout.writeShort(0); // (no ClassFile attributes for proxy classes)  
  108.   } catch (IOException e) {  
  109.       throw new InternalError("unexpected I/O Exception");  
  110.   }  
  111.   return bout.toByteArray();  
  112.      
http://blog.csdn.net/column/details/kernel.html#
http://blog.csdn.net/lycb_gz/article/details/6975720
http://blog.csdn.net/morewindows/article/details/6976468


转载的文章。。。。。

posted @ 2011-11-19 18:32  规格严格-功夫到家  阅读(359)  评论(0编辑  收藏  举报