JAVA动态代理内部实现

源:http://blog.csdn.net/liuhebing/article/details/5571734
评:
一 代理设计模式

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

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

 代理模式类图  

常见的代理有:
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中都使用动态代理。

view plaincopy to clipboardprint?

    interface Subject  
    {  
      public void doSomething();  
    }  
    class RealSubject implements Subject  
    {  
      public void doSomething()  
      {  
        System.out.println( "call doSomething()" );  
      }  
    }  
    class ProxyHandler implements InvocationHandler  
    {  
      private Object proxied;  
        
      public ProxyHandler( Object proxied )  
      {  
        this.proxied = proxied;  
      }  
        
      public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable  
      {  
        return method.invoke( proxied, args);  
      }   
    } 



view plaincopy to clipboardprint?

    import java.lang.reflect.InvocationHandler;  
    import java.lang.reflect.Method;  
    import java.lang.reflect.Proxy;  
    import sun.misc.ProxyGenerator;  
    import java.io.*;  
    public class DynamicProxy  
    {  
      public static void main( String args[] )  
      {  
        RealSubject real = new RealSubject();  
        Subject proxySubject = ( Subject )     
               Proxy.newProxyInstance(   
               Subject.class.getClassLoader(),   
                     new Class[] { Subject.class },   
                       new ProxyHandler( real ) );  
          
        proxySubject.doSomething();  
      //write proxySubject class binary data to file  
        createProxyClassFile();  
      }  
        
      public static void createProxyClassFile()  
      {  
        String name = "ProxySubject";  
        byte[] data = ProxyGenerator.generateProxyClass( name, new Class[] { Subject.class } );  
        try 
        {  
          FileOutputStream out = new FileOutputStream( name + ".class" );  
          out.write( data );  
          out.close();  
        }  
        catch( Exception e )  
        {  
          e.printStackTrace();  
        }  
      }  
    } 


三 动态代理内部实现

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


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

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

反编译后的ProxySubject.java:

view plaincopy to clipboardprint?

    import java.lang.reflect.*;  
    public final class ProxySubject extends Proxy  
        implements Subject  
    {  
        private static Method m1;  
        private static Method m0;  
        private static Method m3;  
        private static Method m2;  
        public ProxySubject(InvocationHandler invocationhandler)  
        {  
            super(invocationhandler);  
        }  
        public final boolean equals(Object obj)  
        {  
            try 
            {  
                return ((Boolean)super.h.invoke(this, m1, new Object[] {  
                    obj  
                })).booleanValue();  
            }  
            catch(Error _ex) { }  
            catch(Throwable throwable)  
            {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
        public final int hashCode()  
        {  
            try 
            {  
                return ((Integer)super.h.invoke(this, m0, null)).intValue();  
            }  
            catch(Error _ex) { }  
            catch(Throwable throwable)  
            {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
        public final void doSomething()  
        {  
            try 
            {  
                super.h.invoke(this, m3, null);  
                return;  
            }  
            catch(Error _ex) { }  
            catch(Throwable throwable)  
            {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
        public final String toString()  
        {  
            try 
            {  
                return (String)super.h.invoke(this, m2, null);  
            }  
            catch(Error _ex) { }  
            catch(Throwable throwable)  
            {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
        static   
        {  
            try 
            {  
                m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {  
                    Class.forName("java.lang.Object")  
                });  
                m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);  
                m3 = Class.forName("Subject").getMethod("doSomething", new Class[0]);  
                m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);  
            }  
            catch(NoSuchMethodException nosuchmethodexception)  
            {  
                throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
            }  
            catch(ClassNotFoundException classnotfoundexception)  
            {  
                throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
            }  
        }  
    } 



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

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

view plaincopy to clipboardprint?

    private byte[] generateClassFile() {  
      /* 
       * Record that proxy methods are needed for the hashCode, equals, 
       * and toString methods of java.lang.Object.  This is done before 
       * the methods from the proxy interfaces so that the methods from 
       * java.lang.Object take precedence over duplicate methods in the 
       * proxy interfaces. 
       */ 
      addProxyMethod(hashCodeMethod, Object.class);  
      addProxyMethod(equalsMethod, Object.class);  
      addProxyMethod(toStringMethod, Object.class);  
      /* 
       * Now record all of the methods from the proxy interfaces, giving 
       * earlier interfaces precedence over later ones with duplicate 
       * methods. 
       */ 
      for (int i = 0; i < interfaces.length; i++) {  
          Method[] methods = interfaces[i].getMethods();  
          for (int j = 0; j < methods.length; j++) {  
        addProxyMethod(methods[j], interfaces[i]);  
          }  
      }  
      /* 
       * For each set of proxy methods with the same signature, 
       * verify that the methods' return types are compatible. 
       */ 
      for (List<ProxyMethod> sigmethods : proxyMethods.values()) {  
          checkReturnTypes(sigmethods);  
      }  
      /* ============================================================ 
       * Step 2: Assemble FieldInfo and MethodInfo structs for all of 
       * fields and methods in the class we are generating. 
       */ 
      try {  
          methods.add(generateConstructor());  
          for (List<ProxyMethod> sigmethods : proxyMethods.values()) {  
        for (ProxyMethod pm : sigmethods) {  
            // add static field for method's Method object  
            fields.add(new FieldInfo(pm.methodFieldName,  
          "Ljava/lang/reflect/Method;",  
           ACC_PRIVATE | ACC_STATIC));  
            // generate code for proxy method and add it  
            methods.add(pm.generateMethod());  
        }  
          }  
          methods.add(generateStaticInitializer());  
      } catch (IOException e) {  
          throw new InternalError("unexpected I/O Exception");  
      }  
      /* ============================================================ 
       * Step 3: Write the final class file. 
       */ 
      /* 
       * Make sure that constant pool indexes are reserved for the 
       * following items before starting to write the final class file. 
       */ 
      cp.getClass(dotToSlash(className));  
      cp.getClass(superclassName);  
      for (int i = 0; i < interfaces.length; i++) {  
          cp.getClass(dotToSlash(interfaces[i].getName()));  
      }  
      /* 
       * Disallow new constant pool additions beyond this point, since 
       * we are about to write the final constant pool table. 
       */ 
      cp.setReadOnly();  
      ByteArrayOutputStream bout = new ByteArrayOutputStream();  
      DataOutputStream dout = new DataOutputStream(bout);  
      try {  
          /* 
           * Write all the items of the "ClassFile" structure. 
           * See JVMS section 4.1. 
           */ 
              // u4 magic;  
          dout.writeInt(0xCAFEBABE);  
              // u2 minor_version;  
          dout.writeShort(CLASSFILE_MINOR_VERSION);  
              // u2 major_version;  
          dout.writeShort(CLASSFILE_MAJOR_VERSION);  
          cp.write(dout);   // (write constant pool)  
              // u2 access_flags;  
          dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);  
              // u2 this_class;  
          dout.writeShort(cp.getClass(dotToSlash(className)));  
              // u2 super_class;  
          dout.writeShort(cp.getClass(superclassName));  
              // u2 interfaces_count;  
          dout.writeShort(interfaces.length);  
              // u2 interfaces[interfaces_count];  
          for (int i = 0; i < interfaces.length; i++) {  
        dout.writeShort(cp.getClass(  
            dotToSlash(interfaces[i].getName())));  
          }  
              // u2 fields_count;  
          dout.writeShort(fields.size());  
              // field_info fields[fields_count];  
          for (FieldInfo f : fields) {  
        f.write(dout);  
          }  
              // u2 methods_count;  
          dout.writeShort(methods.size());  
              // method_info methods[methods_count];  
          for (MethodInfo m : methods) {  
        m.write(dout);  
          }  
                 // u2 attributes_count;  
          dout.writeShort(0); // (no ClassFile attributes for proxy classes)  
      } catch (IOException e) {  
          throw new InternalError("unexpected I/O Exception");  
      }  
      return bout.toByteArray();  
         

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