动态代理

来源:互联网 发布:小米电视 直播软件 编辑:程序博客网 时间:2024/06/14 06:32

      最近工作中遇到一些动态代理的问题,所以在休闲之余总结了一些关于动态代理的知识以供分享:

代理设计模式

定义:为其他对象提供一种代理以控制对这个对象的访问。
代理模式的结构如下图所示。
动态代理使用
java动态代理机制以巧妙的方式实现了代理模式的设计理念。
代理模式示例代码
[Java] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
publicinterface Subject  
{  
  publicvoid doSomething();  
}  
publicclass RealSubject implementsSubject  
{  
  publicvoid doSomething()  
  {  
    System.out.println("call doSomething()" );  
  }  
}  
publicclass ProxyHandler implementsInvocationHandler  
{  
  privateObject proxied;  
      
  publicProxyHandler( Object proxied )  
  {  
    this.proxied = proxied;  
  }  
      
  publicObject invoke( Object proxy, Method method, Object[] args ) throwsThrowable  
  {  
    //在转调具体目标对象之前,可以执行一些功能处理
 
    //转调具体目标对象的方法
    returnmethod.invoke( proxied, args); 
     
    //在转调具体目标对象之后,可以执行一些功能处理
  }   
}
[Java] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
importjava.lang.reflect.InvocationHandler;  
importjava.lang.reflect.Method;  
importjava.lang.reflect.Proxy;  
importsun.misc.ProxyGenerator;  
importjava.io.*;  
publicclass DynamicProxy  
{  
  publicstatic void main( String args[] )  
  {  
    RealSubject real = newRealSubject();  
    Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
     newClass[]{Subject.class},
     newProxyHandler(real));
          
    proxySubject.doSomething();
    
    //write proxySubject class binary data to file  
    createProxyClassFile();  
  }  
      
  publicstatic void createProxyClassFile()  
  {  
    String name = "ProxySubject";  
    byte[] data = ProxyGenerator.generateProxyClass( name, newClass[] { Subject.class} );  
    try 
    {  
      FileOutputStream out = newFileOutputStream( name + ".class");  
      out.write( data );  
      out.close();  
    }  
    catch( Exception e )  
    {  
      e.printStackTrace();  
    }  
  }  
}
动态代理内部实现
首先来看看类Proxy的代码实现 Proxy的主要静态变量
[Java] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
// 映射表:用于维护类装载器对象到其对应的代理类缓存
privatestatic Map loaderToCache = newWeakHashMap();
 
// 标记:用于标记一个动态代理类正在被创建中
privatestatic Object pendingGenerationMarker = newObject();
 
// 同步表:记录已经被创建的动态代理类类型,主要被方法 isProxyClass 进行相关的判断
privatestatic Map proxyClasses = Collections.synchronizedMap(newWeakHashMap());
 
// 关联的调用处理器引用
protectedInvocationHandler h;
Proxy的构造方法
[Java] 纯文本查看 复制代码
?
1
2
3
4
5
// 由于 Proxy 内部从不直接调用构造函数,所以 private 类型意味着禁止任何调用
privateProxy() {}
 
// 由于 Proxy 内部从不直接调用构造函数,所以 protected 意味着只有子类可以调用
protectedProxy(InvocationHandler h) {this.h = h;}
Proxy静态方法newProxyInstance
[Java] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
publicstatic Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h)throwsIllegalArgumentException {
    // 检查 h 不为空,否则抛异常
    if(h == null) {
        thrownew NullPointerException();
    }
 
    // 获得与指定类装载器和一组接口相关的代理类类型对象
    Class cl = getProxyClass(loader, interfaces);
 
    // 通过反射获取构造函数对象并生成代理类实例
    try{
        Constructor cons = cl.getConstructor(constructorParams);
        return(Object) cons.newInstance(newObject[] { h });
    }catch(NoSuchMethodException e) { thrownew InternalError(e.toString());
    }catch(IllegalAccessException e) { thrownew InternalError(e.toString());
    }catch(InstantiationException e) { thrownew InternalError(e.toString());
    }catch(InvocationTargetException e) { thrownew InternalError(e.toString());
    }
}
ProxygetProxyClass方法调用ProxyGenerator的 generateProxyClass方法产生ProxySubject.class的二进制数据:
[Java] 纯文本查看 复制代码
?
1
publicstatic byte[] generateProxyClass(finalString name, Class[] interfaces)
我们可以import sun.misc.ProxyGenerator,调用 generateProxyClass方法产生binary data,然后写入文件,最后通过反编译工具来查看内部实现原理。 反编译后的ProxySubject.java Proxy静态方法newProxyInstance
[Java] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
importjava.lang.reflect.*;  
publicfinal class ProxySubject extendsProxy  
    implementsSubject  
{  
    privatestatic Method m1;  
    privatestatic Method m0;  
    privatestatic Method m3;  
    privatestatic Method m2;  
    publicProxySubject(InvocationHandler invocationhandler)  
    {  
        super(invocationhandler);  
    }  
    publicfinal boolean equals(Object obj)  
    {  
        try 
        {  
            return((Boolean)super.h.invoke(this, m1, newObject[] {  
                obj  
            })).booleanValue();  
        }  
        catch(Error _ex) { }  
        catch(Throwable throwable)  
        {  
            thrownew UndeclaredThrowableException(throwable);  
        }  
    }  
    publicfinal int hashCode()  
    {  
        try 
        {  
            return((Integer)super.h.invoke(this, m0, null)).intValue();  
        }  
        catch(Error _ex) { }  
        catch(Throwable throwable)  
        {  
            thrownew UndeclaredThrowableException(throwable);  
        }  
    }  
    publicfinal void doSomething()  
    {  
        try 
        {  
            super.h.invoke(this, m3, null);  
            return;  
        }  
        catch(Error _ex) { }  
        catch(Throwable throwable)  
        {  
            thrownew UndeclaredThrowableException(throwable);  
        }  
    }  
    publicfinal String toString()  
    {  
        try 
        {  
            return(String)super.h.invoke(this, m2, null);  
        }  
        catch(Error _ex) { }  
        catch(Throwable throwable)  
        {  
            thrownew UndeclaredThrowableException(throwable);  
        }  
    }  
    static   
    {  
        try 
        {  
            m1 = Class.forName("java.lang.Object").getMethod("equals",newClass[] {  
                Class.forName("java.lang.Object")  
            });  
            m0 = Class.forName("java.lang.Object").getMethod("hashCode",newClass[0]);  
            m3 = Class.forName("Subject").getMethod("doSomething",newClass[0]);  
            m2 = Class.forName("java.lang.Object").getMethod("toString",newClass[0]);  
        }  
        catch(NoSuchMethodException nosuchmethodexception)  
        {  
            thrownew NoSuchMethodError(nosuchmethodexception.getMessage());  
        }  
        catch(ClassNotFoundException classnotfoundexception)  
        {  
            thrownew NoClassDefFoundError(classnotfoundexception.getMessage());  
        }  
    }  
}
ProxyGenerator内部是如何生成class二进制数据,可以参考源代码。
[Java] 纯文本查看 复制代码
?
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
privatebyte[] 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(inti = 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) {  
      thrownew InternalError("unexpected I/O Exception");  
  }  
  returnbout.toByteArray();
总结
一个典型的动态代理创建对象过程可分为以下四个步骤:
1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(...);
2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。
生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))
美中不足
诚然,Proxy已经设计得非常优美,但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持interface代理的桎梏,因为它的设计注定了这个遗憾。回想一下那些动态生成的代理类的继承关系图,它们已经注定有一个共同的父类叫Proxy。Java的继承机制注定了这些动态代理类们无法实现对class的动态代理,原因是多继承在Java中本质上就行不通。有很多条理由,人们可以否定对 class代理的必要性,但是同样有一些理由,相信支持class动态代理会更美好。接口和类的划分,本就不是很明显,只是到了Java中才变得如此的细化。如果只从方法的声明及是否被定义来考量,有一种两者的混合体,它的名字叫抽象类。实现对抽象类的动态代理,相信也有其内在的价值。此外,还有一些历史遗留的类,它们将因为没有实现任何接口而从此与动态代理永世无缘。如此种种,不得不说是一个小小的遗憾。但是,不完美并不等于不伟大,伟大是一种本质,Java动态代理就是佐例。
原创粉丝点击