动态代理

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

代理设计模式

定义:为其他对象提供一种代理以控制对这个对象的访问。
代理模式的结构如下图所示。
动态代理使用
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
public interface Subject  
{  
   public void doSomething();  
}  
public class RealSubject implements Subject  
{  
   public void doSomething()  
   {  
     System.out.println( "call doSomething()" );  
   }  
}  
public 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); 
     
     //在转调具体目标对象之后,可以执行一些功能处理
   }   
}
[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
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的代码实现 Proxy的主要静态变量
[Java]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
// 映射表:用于维护类装载器对象到其对应的代理类缓存
private static Map loaderToCache = new WeakHashMap();
 
// 标记:用于标记一个动态代理类正在被创建中
private static Object pendingGenerationMarker = new Object();
 
// 同步表:记录已经被创建的动态代理类类型,主要被方法 isProxyClass 进行相关的判断
private static Map proxyClasses = Collections.synchronizedMap( new WeakHashMap());
 
// 关联的调用处理器引用
protected InvocationHandler h;
Proxy的构造方法
[Java]  纯文本查看  复制代码
?
1
2
3
4
5
// 由于 Proxy 内部从不直接调用构造函数,所以 private 类型意味着禁止任何调用
private Proxy() {}
 
// 由于 Proxy 内部从不直接调用构造函数,所以 protected 意味着只有子类可以调用
protected Proxy(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
public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h) throws IllegalArgumentException {
     // 检查 h 不为空,否则抛异常
     if (h == null ) {
         throw new NullPointerException();
     }
 
     // 获得与指定类装载器和一组接口相关的代理类类型对象
     Class cl = getProxyClass(loader, interfaces);
 
     // 通过反射获取构造函数对象并生成代理类实例
     try {
         Constructor cons = cl.getConstructor(constructorParams);
         return (Object) cons.newInstance( new Object[] { h });
     } catch (NoSuchMethodException e) { throw new InternalError(e.toString());
     } catch (IllegalAccessException e) { throw new InternalError(e.toString());
     } catch (InstantiationException e) { throw new InternalError(e.toString());
     } catch (InvocationTargetException e) { throw new InternalError(e.toString());
     }
}
ProxygetProxyClass方法调用ProxyGenerator的 generateProxyClass方法产生ProxySubject.class的二进制数据:
[Java]  纯文本查看  复制代码
?
1
public static byte [] generateProxyClass( final String 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
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());  
         }  
     }  
}
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
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();
总结
一个典型的动态代理创建对象过程可分为以下四个步骤:
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动态代理就是佐例。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值