动态代理的一点理解

程序设计里面有一种设计模式叫做代理模式。“代理”顾名思义,就是你想做一件事,但是不想自己去做,这个时候可以委托别人去帮你办理这件事。

话不多说,下面以Java代码来举例说明。

1 Subject.java 
2 package staticproxy.learn;
3 
4 interface Subject {
5     public void doSomething();
6 }
1 RealSubject.java
2 package staticproxy.learn;
3 
4 class RealSubject implements Subject {
5     public void doSomething() {
6         System.out.println("doSomething()");
7     }
8 }
1 Client.java
2 package staticproxy.learn;
3 
4 public class Client {
5     public static void main(String args[]) {
6         Subject real = new RealSubject();
7         real.doSomething();
8     }
9 }

输出结果:

doSomething()

如果我们想计算doSomething()执行的时间是多少,怎么办呢?我们想到最简单的一种方法就是修改doSomething()这个方法,如下:

 1 RealSubject.java
 2 package staticproxy.learn;
 3 
 4 class RealSubject implements Subject {
 5     public void doSomething() {
 6         long startTime=System.nanoTime();
 7         System.out.println("doSomething()");
 8         long endTime=System.nanoTime();        
9 System.out.println("程序运行时间: "+(endTime-startTime)+"ns"); 10 } 11 }
1 Client.java
2 package staticproxy.learn;
3 
4 public class Client {
5     public static void main(String args[]) {
6         Subject real = new RealSubject();
7         real.doSomething();
8     }
9 }

输出结果:

doSomething()
程序运行时间: 141512ns

方法2:

增加一个类叫做ProxySubject.java,让它也实现Subject这个接口,然后实现doSomething()的这个方法,在这个方法里面把计算程序运行时间的代码添加上,如下:

 1 ProxySubject.java
 2 package staticproxy.learn;
 3 
 4 public class ProxySubject implements Subject {
 5 
 6     private Subject object = new RealSubject();
 7 
 8     @Override
 9     public void doSomething() {
10         long startTime=System.nanoTime();
11         object.doSomething();
12         long endTime=System.nanoTime();
13         System.out.println("程序运行时间: "+(endTime-startTime)+"ns");
14     }
15 }
1 Client.java
2 package staticproxy.learn;
3 
4 public class Client {
5     public static void main(String args[]) {
6         ProxySubject ps = new ProxySubject();
7         ps.doSomething();
8     }
9 }

输出结果:

doSomething()
程序运行时间: 134980ns

方法2有一个称呼叫做静态代理,所有的操作都委托ProxySubject这个类来完成。

之所以叫做静态代理,是因为代理类是我们手动写代码来完成的。

方法1,2的缺点:如果Subject这个接口里面有很多方法,那么是不是实现类里面也要相应的修改多少个,这样会导致类的急剧膨胀,显然不是最好的方法。

还有没有更好的方法来实现上面的需求呢,当然有,相当于静态代理,动态代理就应运而生了。

话不多说,直接看效果。

1 Subject.java
2 package dynamic.proxy.learn;
3  
4 interface Subject {
5       public void doSomething();
6 }
1 RealSubject.java
2 package staticproxy.learn;
3  
4 class RealSubject implements Subject {
5       public void doSomething() {
6            System.out.println("doSomething()");
7       }
8 }

一个实现了InvocationHandler接口的实际实现类,这个接口里面只有一个方法invoke()

 1 package dynamic.proxy.learn;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 
 6 class ProxyHandler implements InvocationHandler {
 7     private Object proxied;
 8 
 9     public ProxyHandler(Object proxied) {
10         this.proxied = proxied;
11     }
12 
13     @Override
14     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
15         long startTime=System.nanoTime();
16         Object rs = method.invoke(proxied, args);
17         long endTime=System.nanoTime();
18         System.out.println("程序运行时间: "+(endTime-startTime)+"ns");
19         return rs;
20     }
21 }
 1 Client.java
 2 package dynamic.proxy.learn;
 3  
 4 import java.lang.reflect.Proxy;
 5  
 6 public class Client {
 7       public static void main(String args[]) {
 8            Subject real = new RealSubject();
 9            Subject proxySubject = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[] { Subject.class }, new ProxyHandler(real));
10            proxySubject.doSomething();
11       }
12 }

输出结果:

doSomething()
程序运行时间: 311015ns

程序达到了同样的效果,这里估计都有2个疑问。

1,ProxyHandler里面的invoke方法究竟是被谁调用的?

2,Subject proxySubject = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[] { Subject.class }, new ProxyHandler(real));

这里的proxySubject对象究竟是怎么样的?

带着这些疑问,先抛出答案,再行分析究竟是怎么回事。

原来程序在执行的时候,会自动生成一个$proxy1.java(实际名字前面有包名,后面有num)的实际代理类,这个类的主要代码如下:

 1 package dynamic.proxy.learn;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.Proxy;
 6 import java.lang.reflect.UndeclaredThrowableException;
 7 
 8 public final class ProxySubject extends Proxy implements Subject {
 9 
10     private static final long serialVersionUID = 1L;
11     private static Method m1;
12     private static Method m0;
13     private static Method m3;
14     private static Method m2;
15     static {
16         try {
17             m1 = Class.forName("java.lang.Object").getMethod("equals",
18                     new Class[] { Class.forName("java.lang.Object") });
19             m0 = Class.forName("java.lang.Object").getMethod("hashCode",
20                     new Class[0]);
21             m3 = Class.forName("jdbctest.Subject").getMethod("doSomething",
22                     new Class[0]);
23             m2 = Class.forName("java.lang.Object").getMethod("toString",
24                     new Class[0]);
25             return;
26         } catch (NoSuchMethodException localNoSuchMethodException) {
27             throw new
28             NoSuchMethodError(localNoSuchMethodException.getMessage());
29         } catch (ClassNotFoundException localClassNotFoundException) {
30             throw new
31             NoClassDefFoundError(localClassNotFoundException.getMessage());
32         }
33     }
34 
35     public ProxySubject(InvocationHandler paramInvocationHandler)
36 
37     {
38         super(paramInvocationHandler);
39     }
40 
41     public final void doSomething()
42 
43     {
44         try {
45             this.h.invoke(this, m3, null);
46             return;
47         } catch (Error | RuntimeException localError) {
48             throw localError;
49         } catch (Throwable localThrowable) {
50             throw new UndeclaredThrowableException(localThrowable);
51         }
52     }
53 
54     public final boolean equals(Object paramObject)
55 
56     {
57         try {
58             return ((Boolean) this.h.invoke(this, m1,
59                     new Object[] { paramObject })).booleanValue();
60         } catch (Error | RuntimeException localError) {
61             throw localError;
62         } catch (Throwable localThrowable) {
63             throw new UndeclaredThrowableException(localThrowable);
64         }
65     }
66 
67     public final int hashCode()
68 
69     {
70         try {
71             return ((Integer) this.h.invoke(this, m0, null)).intValue();
72         } catch (Error | RuntimeException localError) {
73             throw localError;
74         } catch (Throwable localThrowable) {
75             throw new UndeclaredThrowableException(localThrowable);
76         }
77     }
78 
79 
80 
81     public final String toString()
82 
83     {
84         try {
85             return (String) this.h.invoke(this, m2, null);
86         } catch (Error | RuntimeException localError) {
87             throw localError;
88         } catch (Throwable localThrowable) {
89             throw new UndeclaredThrowableException(localThrowable);
90         }
91     }
92 }

这个类里面有一个带参数的构造方法,这个参数需要传递实现了接口InvocationHandler的ProxyHandler的实例,然后这个类里面有一个同样的doSomething()方法,在这个方法里面有一句this.h.invoke(this, m3, null);正是这一句代码,ProxyHandler里面的invoke方法才得以调用。

接下来继续分析源码,看它是如何动态实现的。

1         Subject proxySubject = (Subject) Proxy.newProxyInstance(
2                 Subject.class.getClassLoader(), new Class[] { Subject.class },
3                 new ProxyHandler(real));

Proxy.newProxyInstance的分析
Proxy.newProxyInstance这个方法里面一共有三个参数:
* ClassLoader loader : 指定被代理对象的类加载器
* Class<?>[] interfaces: 指定被代理对象所实现的接口
* InvocationHandler h: 指定需要调用的InvocationHandler对象

 1 public static Object newProxyInstance(ClassLoader loader,
 2                                           Class<?>[] interfaces,
 3                                           InvocationHandler h)
 4         throws IllegalArgumentException
 5     {
 6         // h不能为空
 7         Objects.requireNonNull(h);
 8         // 安全检查
 9         final SecurityManager sm = System.getSecurityManager();
10         if (sm != null) {
11             checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);
12         }
13 
14         // 主要的代理就是这一句,生成代理类
15         Class<?> cl = getProxyClass0(loader, interfaces);
16         try {
17             if (sm != null) {
18                 checkNewProxyPermission(Reflection.getCallerClass(), cl);
19             }
20             // 取得代理类的构造函数(不懂的同学,请学习一下Java的反射)
21             final Constructor<?> cons = cl.getConstructor(constructorParams);
22             final InvocationHandler ih = h;
23             if (!Modifier.isPublic(cl.getModifiers())) {
24                 AccessController.doPrivileged(new PrivilegedAction<Void>() {
25                     public Void run() {
26                         cons.setAccessible(true);
27                         return null;
28                     }
29                 });
30             }
31             // 取得代理类的实例
32             return cons.newInstance(new Object[]{h});
33         } catch (IllegalAccessException|InstantiationException e) {
34             throw new InternalError(e.toString(), e);
35         } catch (InvocationTargetException e) {
36             Throwable t = e.getCause();
37             if (t instanceof RuntimeException) {
38                 throw (RuntimeException) t;
39             } else {
40                 throw new InternalError(t.toString(), t);
41             }
42         } catch (NoSuchMethodException e) {
43             throw new InternalError(e.toString(), e);
44         }
45     } 
getProxyClass0方法的分析
 1     /**
 2      * Generate a proxy class.  Must call the checkProxyAccess method
 3      * to perform permission checks before calling this.
 4      */
 5     private static Class<?> getProxyClass0(ClassLoader loader,
 6                                            Class<?>... interfaces) {
 7         // 被代理对象的接口数量不能超过65535个,否则抛出错误
 8         if (interfaces.length > 65535) {
 9             throw new IllegalArgumentException("interface limit exceeded");
10         }
11 
12         // If the proxy class defined by the given loader implementing
13         // the given interfaces exists, this will simply return the cached copy;
14         // otherwise, it will create the proxy class via the ProxyClassFactory
15         // 简单一点就是说,缓存里面存在代理就从里面取,没有就创建一个新的代理对象
16         return proxyClassCache.get(loader, interfaces);
17     }

再看看这个get方法

1 public V get(K key, P parameter) {
2         Objects.requireNonNull(parameter);
3         ...
4         Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
5         ...
6     }

这里的subKeyFactory是实现了BiFunction接口的ProxyClassFactory的一个实例

ProxyClassFactory分析

 1     private static final class ProxyClassFactory
 2         implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
 3         // 代理类名字的前缀
 4         private static final String proxyClassNamePrefix = "$Proxy";
 5 
 6         // 计数器
 7         private static final AtomicLong nextUniqueNumber = new AtomicLong();
 8 
 9         @Override
10         public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
11 
12             String proxyPkg = null;
13             // 非公共接口,代理类的包名与接口的相同
14             for (Class<?> intf : interfaces) {
15                 int flags = intf.getModifiers();
16                 if (!Modifier.isPublic(flags)) {
17                     String name = intf.getName();
18                     int n = name.lastIndexOf('.');
19                     String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
20                     if (proxyPkg == null) {
21                         proxyPkg = pkg;
22                     } else if (!pkg.equals(proxyPkg)) {
23                         throw new IllegalArgumentException(
24                             "non-public interfaces from different packages");
25                     }
26                 }
27             }
28 
29             // 对于公共接口的包名,默认为com.sun.proxy
30             if (proxyPkg == null) {
31                 proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
32             }
33 
34             // 取得计数
35             long num = nextUniqueNumber.getAndIncrement();
36             // 代理类的名字
37             String proxyName = proxyPkg + proxyClassNamePrefix + num;
38 
39             // 生成代理类的字节码
40             byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
41                 proxyName, interfaces);
42             try {
43                 // 根据二进制字节码返回相应的Class实例
44                 return defineClass0(loader, proxyName,
45                                     proxyClassFile, 0, proxyClassFile.length);
46             } catch (ClassFormatError e) {
47                 throw new IllegalArgumentException(e.toString());
48             }
49         }
50     }

// 生成代理类的字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);

这部分代码没有开源,实现的功能就是会生成代理类的*.class文件,并且返回字节码数组。

以上就是动态代理的全部实现过程。

 

posted @ 2016-10-19 09:39  苦味如昔  阅读(193)  评论(0编辑  收藏  举报