`
MauerSu
  • 浏览: 509290 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

JAVA动态代理内部实现

    博客分类:
  • J2SE
 
阅读更多
源: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();  
         
分享到:
评论

相关推荐

    模拟JDK动态代理内部实现

    在本文中,我们将深入探讨如何模拟JDK的动态代理内部实现。 首先,我们需要了解JDK动态代理的基础知识。Java中的动态代理通过`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口实现。`Proxy...

    java动态代理机制

    `Proxy` 类是Java提供的一个内部类,它主要用于创建一个实现了指定一组接口的动态代理类实例。使用Proxy类,我们可以通过传入InvocationHandler接口的实现来控制代理对象的行为。Proxy类提供了静态方法`...

    Java 动态代理详解(学习资料)

    CGLIB 是通过继承目标类的方式来实现动态代理的,因此目标类不能为 final 类。这里不再详细介绍 CGLIB 的具体实现,因为它相比 JDK 动态代理更为复杂,需要额外的库支持。总结Java 动态代理为我们提供了在运行时创建...

    java动态代理(2)

    Java动态代理机制是Java语言提供的一种强大的功能,它允许在运行时创建代理对象来实现指定的接口。这一机制使得我们可以在不修改已有代码的情况下,为已有接口增加额外的功能或者增强已有功能的行为。在Java中,动态...

    java动态代理例子

    CGLIB通过字节码技术生成子类来实现动态代理。 1. **创建Enhancer对象**:CGLIB的核心是`net.sf.cglib.proxy.Enhancer`类,它是CGLIB代理的工厂类。通过Enhancer,我们可以设置回调对象(相当于JDK动态代理中的...

    Java中的动态代理

    ### Java中的动态代理知识点解析 #### 一、动态代理概念 在Java中,动态代理是一种设计模式,它允许我们创建一个对象来代表另一个对象。这种模式通常用于在不修改目标对象的情况下,为对象添加额外的功能或者行为...

    java-用Java动态代理实现AOP.pdf

    Java动态代理机制要求被代理的类必须实现至少一个接口,因为Java动态代理是通过JDK的java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现的。Proxy类用于生成代理对象,而InvocationHandler...

    JAVA 反射机制与动态代理ppt

    而动态代理则是在运行时动态生成代理类,通常使用`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口来实现。动态代理常用于事件处理、事务管理、日志记录等场景,它可以在方法调用前后添加...

    java事务 - 使用动态代理

    - Spring通过两种方式实现动态代理:JDK动态代理和CGLIB代理。前者适用于接口实现类,后者适用于没有接口的类。 - 使用`@Transactional`注解标记在方法上,Spring会自动为这些方法添加事务边界,开始事务、提交...

    Java代理服务器的实现

    在这个主题中,我们将深入探讨如何在Java中实现一个基本的代理服务器。 首先,`HttpProxy` 和 `SubHttpProxy` 类名暗示了我们可能在处理HTTP协议的代理服务。HTTP代理服务器通常接收来自客户端的HTTP请求,然后转发...

    Java中动态代理的介绍及实例

    在JDK中,动态代理主要通过`java.lang.reflect.Proxy`类和`InvocationHandler`接口实现。关键步骤如下: 1. **创建InvocationHandler实现类**:用户自定义实现`InvocationHandler`接口,这个实现类负责处理代理对象...

    Java反射与动态代理

    6. **实现插件化和框架设计**:许多框架如Spring、Hibernate都大量使用反射,实现动态配置和代码解耦。 动态代理是Java提供的一种高级特性,主要用于在运行时创建一个类的代理对象,这个代理对象可以拦截并增强原...

    关于jdk动态代理的源码剖析

    本文将深入分析JDK动态代理的工作原理及其内部实现机制。 #### 二、为什么需要JDK动态代理? 在实际开发中,经常会遇到需要为已有的类添加新功能的需求,但又不能直接修改这些类的源码。此时,动态代理技术就显得...

    Struts2 拦截器 和 Java 中的动态代理

    Java的`java.lang.reflect.InvocationHandler`接口和`java.lang.reflect.Proxy`类是实现动态代理的关键。InvocationHandler接口定义了一个`invoke`方法,该方法会在代理对象的方法被调用时执行。Proxy类则负责生成...

    java动态代理1

    总结来说,Java动态代理是通过`InvocationHandler`和`Proxy`类实现的,它让我们能够在运行时创建一个能够代理指定接口的类,并且在调用方法时添加额外的逻辑。在实际应用中,这可以用于日志记录、性能分析、事务管理...

    JAVA动态代理模式.pdf

    在Java中,动态代理是利用反射机制实现的,可以在运行时动态生成代理类和对象。它主要应用于需要控制对象访问、提供额外功能(如日志、权限检查等)的场景中。 在给定文件的内容中,主要涉及到两个关键部分,分别是...

    Java动态代理1

    通过动态代理,我们可以创建一个实现了`NewInterface`的代理类,让它在内部调用`OldInterface`的方法,实现接口间的转换。 ```java public class OldSystemAdapter implements NewInterface, InvocationHandler { ...

    经典java反射和动态代理代码

    Java反射和动态代理是Java编程中的重要特性,它们允许程序在运行时检查和操作类、接口、对象等的内部结构,以及动态地创建代理对象来处理特定任务。在这篇文章中,我们将深入探讨这两个主题,结合"reflections"这个...

    jdk与cglib动态代理与底层实现

    JDK和CGLIB是Java中实现动态代理的两种主要方式,它们在Spring框架中扮演着关键角色,尤其是在AOP(面向切面编程)中。 1. **JDK动态代理**: JDK动态代理基于Java的接口机制实现,因此,要使用JDK动态代理,被...

Global site tag (gtag.js) - Google Analytics