JAVA动态代理内部实现

http://blog.csdn.net/liuhebing/article/details/5571734



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();  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值