Fork me on GitHub

设计模式之代理模式

设计模式之代理模式

本篇是设计模式系列的第二篇,上一篇单例模式见这里;

首先我们都知道代理也分为静态代理动态代理。虽然现在像spring这样成熟的框架会有自己的实现方式,开箱即用,我们不用自己实现代理,但是对于深入了解学习还是很有必要的。不管是静态代理还是动态代理,两种代理从虚拟机加载类的角度来讲,本质上都是一样的,都是在原有类的行为基础上,加入一些多出的行为,甚至完全替换原有的行为。

一 静态代理

静态代理采用的方式就是我们手动的将这些行为换进去,然后让编译器帮我们编译,同时也就将字节码在原有类的基础上加入一些其他的东西或者替换原有的东西,产生一个新的与原有类接口相同但是行为却不同的类型。

下面做个比较常见得数据库连接池的例子,实际体会下这种方式的代理。

我们都知道,数据库连接是很珍贵的资源,频繁的开关数据库连接是非常浪费服务器的CPU资源以及内存的,所以我们一般都是使用数据库连接池来解决这一问题,即创造一堆等待被使用的连接,等到用的时候就从池里取一个,不用了再放回去,数据库连接在整个应用启动期间,几乎是不关闭的,除非是超过了最大闲置时间。

解决这一问题的办法就是使用代理模式,因为代理模式可以替代原有类的行为,所以我们要做的就是替换掉connection的close行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ConnectionProxy implements Connection {

private Connection connection;
private ConnectionProxy(Connection connection){
super();
this.connection =connection;
}

@Override
public Statement createStatement() throws SQLException {
return connection.createStatement();
}

@Override
public void close() {
System.out.println("----不关闭连接,返回给连接池");
System.out.println("----不关闭连接,返回给连接池");
//connection.close();
DatasourcePool.getInstance().recoveryConnection(connection);
}
}

首先实现原来的Connection接口,因为接口比较多,这里就简单实例下,其他的不变。实际情况中根据自己的需要重写接口。

1
2
3
4
5
6
7
8
9
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
public class DatasourcePool {
//定义初始化时创建的空闲连接数
private final static int initialConnetion=10;
//定义连接容器
private static List<Connection> pools =new Vector<Connection>();


private DatasourcePool(){
if(pools ==null || pools.size()==0){
for(int i =0;i<initialConnetion;i++){
try {
pools.add(createNewConnection());
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}

//加载驱动
static{
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//创建真正的连接对象
private static Connection createNewConnection() throws SQLException {
return DriverManager.getConnection("url","username", "password");
}
//获取连接,这里返回的连接对象必须是我们的代理连接对象.
public static Connection getConnetion(){
if(pools.size()>0){
//避免获取到原始的connetion,这样可以调用close 关闭连接了。
return new ConnectionProxy(pools.remove(0));
}
return null;
}


public static DatasourcePool getInstance(){
return DatasourceInstance.datasourcePool;
}

private static class DatasourceInstance{
private static DatasourcePool datasourcePool= new DatasourcePool();

}

//ConnetionProxy 的重写的close方法,就不用调用原来的close去真正关闭了,
// 而是调用这个方法返回给连接池
public void recoveryConnection(Connection connection){
pools.add(connection);
}


}

好了,这下我们的连接池返回的连接全是代理,就算程序员调用了close方法也只会归还给连接池了。

我们使用代理模式解决了上述问题,从静态代理的使用上来看,我们一般是这么做的。

1. 代理类一般要持有一个被代理的对象的引用。

2. 对于我们不关心的方法,全部委托给被代理的对象处理。

3. 自己处理我们关心的方法。

这种代理是死的,不会在运行时动态创建,因为我们相当于在编译期,也就是你在本地编辑完代码后,就给被代理的对象生成了一个不可动态改变的代理类。

静态代理对于这种,被代理的对象很固定,我们只需要去代理一个类或者若干固定的类,数量不是太多的时候,可以使用,而且其实效果比动态代理更好,因为动态代理就是在运行期间动态生成代理类,所以需要消耗的时间会更久一点。就像上述的情况,其实就比较适合使用静态代理。

下面介绍下动态代理,动态代理是JDK自带的功能,它需要你去实现一个InvocationHandler接口,并且调用Proxy的静态方法去产生代理类。

接下来我们依然使用上面的示例,但是这次该用动态代理处理,我们来试一下看如何做。

二 动态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class ConnetionDymicProxy implements InvocationHandler {

private Connection connection;

public ConnetionDymicProxy(Connection connection){
super();
this.connection =connection;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理Connetion的close方法:
if(proxy.getClass().isAssignableFrom(Connection.class)
&& method.getName().equals("close")){
//return method.invoke(connetion,args);

DatasourcePool.getInstance().recoveryConnection(connection);
return null;
}

return null;
}

private Connection getConnectionProxy(){
return (Connection)Proxy.newProxyInstance(Connection.class.getClassLoader(),new Class[]{Connection.class},this);
}
}

上面是我们针对connection写的动态代理,InvocationHandler接口只有一个invoke方法需要实现,这个方法是用来在生成的代理类用回调使用的,关于动态代理的原理一会做详细的分析,这里我们先只关注用法。很显然,动态代理是将每个方法的具体执行过程交给了我们在invoke方法里处理。而具体的使用方法,我们只需要创造一个ConnectionProxy的实例,并且将调用getConnectionProxy方法的返回结果作为数据库连接池返回的连接就可以了。

上述便是我们针对connection做动态代理的方式,但是我们从中得不到任何好处,除了能少写点代码以外,因为这个动态代理还是只能代理Connection这一个接口,如果我们写出这种动态代理的方式的话,说明我们应该使用静态代理处理这个问题,因为它代表我们其实只希望代理一个类就好。从重构的角度来说,其实更简单点,那就是在你发现你使用静态代理的时候,需要写一大堆重复代码的时候,就请改用动态代理试试吧。

通常情况下,动态代理的使用是为了解决这样一种问题,就是我们需要代理一系列类的某一些方法,最典型的应用就是我们前段时间讨论过的springAOP,我们需要创造出一批代理类,切入到一系列类当中的某一些方法中。下面给出一个经常使用的动态代理方式。

1
2
3
4
5
6
7
8
9
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
public class DynamicProxy implements InvocationHandler{

private Object source;

public DynamicProxy(Object source) {
super();
this.source = source;
}

public void before(){
System.out.println("在方法前做一些事,比如打开事务");
}

public void after(){
System.out.println("在方法返回前做一些事,比如提交事务");
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//假设我们切入toString方法,其他其实也是类似的,一般我们这里大部分是针对特定的方法做事情的,通常不会对类的全部方法切入
//比如我们常用的事务管理器,我们通常配置的就是对save,update,delete等方法才打开事务
if (method.getName().equals("toString")) {
before();
}
Object result = method.invoke(source, args);
if (method.getName().equals("toString")) {
after();
}
return result;
}

public Object getProxy(){
return Proxy.newProxyInstance(getClass().getClassLoader(), source.getClass().getInterfaces(), this);
}


}

上述我做了一些注释,其实已经说明一些问题,这个代理类的作用是可以代理任何类,因为它被传入的对象是Object,而不再是具体的类,比如刚才的Connection,这些产生的代理类在调用toString方法时会被插入before方法和after方法。

动态代理有一个强制性要求,就是被代理的类必须实现了某一个接口,或者本身就是接口,就像我们的Connection。

道理其实很简单,这是因为动态代理生成的代理类是继承Proxy类的,并且会实现被你传入newProxyInstance方法的所有接口,所以我们可以将生成的代理强转为任意一个代理的接口或者Proxy去使用,但是Proxy里面几乎全是静态方法,没有实例方法,所以转换成Proxy意义不大,几乎没什么用。假设我们的类没有实现任何接口,那么就意味着你只能将生成的代理类转换成Proxy,那么就算生成了,其实也没什么用,而且就算你传入了接口,可以强转,你也用不了这个没有实现你传入接口的这个类的方法。

你可能会说,假设有个接口A,那我将接口A传给newProxyInstance方法,并代理一个没实现接口A的类B,但类B与接口A有一样的方法可以吗?

答案是可以的,并且JDK的动态代理只认你传入的接口,只要你传入,你就可以强转成这个接口,这个一会解释,但是你无法在invoke方法里调用method.invoke方法,也就是说,你只能全部替换A接口的方法,而不能使用类B中原有与接口A方法描述相同的方法,这是因为invoke中传入的Method的class信息是接口A,而类B因为没实现接口A,所以无法执行传入的Method,会抛出非法参数异常。

下面我贴出测试代码,各位可以自己试一下,具体为何会这样是在后面解释的,这里不再多做解释。

1
2
3
4
5
6
7
8
9
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
public interface TestInterface {

void method1();

void method2();

void method3();
}

public class TestClass{

public void method1() {
System.out.println("TestClass.method1");
}

public void method2() {
System.out.println("TestClass.method2");
}

public void method3() {
System.out.println("TestClass.method3");
}

}


public class DynamicProxy implements InvocationHandler{

private Object source;

public DynamicProxy(Object source) {
super();
this.source = source;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("接口的方法全部变成这样了");
//这里source是TestClass,但是我们不能使用反射调用它的方法,像下面这样,放开这一行会抛异常
//return method.invoke(source, args);
return null;
}

public static void main(String[] args) {
//只要你传入就可以强转成功
TestInterface object = (TestInterface) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{TestInterface.class}, new DynamicProxy(new TestClass()));
object.method1();
object.method2();
object.method3();
}
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Method sourceMethod = source.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());
sourceMethod.setAccessible(true);
Object result = sourceMethod.invoke(source, args);
System.out.println("after");
return result;
}

这就与你实现接口的表现行为一致了,但是我们本来就只需要一句method.invoke就可以了,就因为没实现接口就要多写两行,所以这种突破JDK动态代理必须实现接口的行为就有点画蛇添足了。因为你本来就实现了该接口的方法,只是差了那一句implements而已。

好了,从现在开始,我们开始详细讲解动态代理的原理,这算是进阶篇:

接下来我们结合源码去看一下,代理类是如何产生的,首先当然就是要进入Proxy的newProxyInstance方法,这里是产生代理的入口,源码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}

/*
* Look up or generate the designated proxy class.
*/
Class cl = getProxyClass(loader, interfaces);

/*
* Invoke its constructor with the designated invocation handler.
*/
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());
}
}

这个方法其实很简单,首先获取了代理类的运行时Class引用,然后调用了这个Class中的构造方法,这个构造方法只有一个参数,正是InvocationHandler接口,由此产生了一个代理类的实例。那么关键的地方就在于如何获取的代理类运行时的class信息的呢?我们进入getProxyClass方法看一下。为了方便起见,我直接加注释,这个方法需要解释的地方比较多。

1
2
3
4
5
6
7
8
9
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces) throws IllegalArgumentException {
//如果传入的接口长度大于65535就抛出异常,我去你妹。。。
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}

Class proxyClass = null;

/* collect interface names to use as key for proxy class cache */
String[] interfaceNames = new String[interfaces.length];

Set interfaceSet = new HashSet(); // for detecting duplicates

for (int i = 0; i < interfaces.length; i++) {
/*
* Verify that the class loader resolves the name of this interface
* to the same Class object.
*/
String interfaceName = interfaces[i].getName();
Class interfaceClass = null;
try {
//加载每一个接口的运行时Class信息
interfaceClass = Class.forName(interfaceName, false, loader);
} catch (ClassNotFoundException e) {
}
//如果采用你传入的类加载器载入的Class和你传入的Class不相等则抛出异常
if (interfaceClass != interfaces[i]) {
throw new IllegalArgumentException(interfaces[i]
+ " is not visible from class loader");
}

//如果你传入的不是接口抛出异常
/*
* Verify that the Class object actually represents an interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(interfaceClass.getName()
+ " is not an interface");
}

//如果你传入的接口重复抛异常
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.contains(interfaceClass)) {
throw new IllegalArgumentException("repeated interface: "
+ interfaceClass.getName());
}
interfaceSet.add(interfaceClass);

interfaceNames[i] = interfaceName;
}

/*
* Using string representations of the proxy interfaces as keys in the
* proxy class cache (instead of their Class objects) is sufficient
* because we require the proxy interfaces to be resolvable by name
* through the supplied class loader, and it has the advantage that
* using a string representation of a class makes for an implicit weak
* reference to the class.
*/
Object key = Arrays.asList(interfaceNames);

/*
* Find or create the proxy class cache for the class loader.
*/
Map cache;
synchronized (loaderToCache) {
//这个是为了存储每一个类加载器所载入过的代理接口的代理类
cache = (Map) loaderToCache.get(loader);
if (cache == null) {
cache = new HashMap();
loaderToCache.put(loader, cache);
}
/*
* This mapping will remain valid for the duration of this method,
* without further synchronization, because the mapping will only be
* removed if the class loader becomes unreachable.
*/
}

/*
* Look up the list of interfaces in the proxy class cache using the
* key. This lookup will result in one of three possible kinds of
* values: null, if there is currently no proxy class for the list of
* interfaces in the class loader, the pendingGenerationMarker object,
* if a proxy class for the list of interfaces is currently being
* generated, or a weak reference to a Class object, if a proxy class
* for the list of interfaces has already been generated.
*/
synchronized (cache) {
/*
* Note that we need not worry about reaping the cache for entries
* with cleared weak references because if a proxy class has been
* garbage collected, its class loader will have been garbage
* collected as well, so the entire cache will be reaped from the
* loaderToCache map.
*/
do {
//检查是否有生成好的代理
Object value = cache.get(key);
if (value instanceof Reference) {
proxyClass = (Class) ((Reference) value).get();
}
//有的话直接返回
if (proxyClass != null) {
// proxy class already generated: return it
return proxyClass;
//否则看一下这个代理类是不是正在构造中,是的话就在cache对象上等待
} else if (value == pendingGenerationMarker) {
// proxy class being generated: wait for it
try {
cache.wait();
} catch (InterruptedException e) {
/*
* The class generation that we are waiting for should
* take a small, bounded time, so we can safely ignore
* thread interrupts here.
*/
}
continue;
//如果没有现成的,也没有创造中的,那就开始创造代理类
} else {
/*
* No proxy class for this list of interfaces has been
* generated or is being generated, so we will go and
* generate it now. Mark it as pending generation.
*/
//将当前代理类置为正在构造中,并直接退出循环
cache.put(key, pendingGenerationMarker);
break;
}
} while (true);
}

try {
String proxyPkg = null; // package to define proxy class in

//这一段是看你传入的接口中有没有不是public的接口,如果有,这些接口必须全部在一个包里定义的,否则抛异常
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that all
* non-public proxy interfaces are in the same package.
*/
for (int i = 0; i < interfaces.length; i++) {
int flags = interfaces[i].getModifiers();
if (!Modifier.isPublic(flags)) {
String name = interfaces[i].getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}

if (proxyPkg == null) { // if no non-public proxy interfaces,
proxyPkg = ""; // use the unnamed package
}

{
/*
* Choose a name for the proxy class to generate.
*/
long num;
synchronized (nextUniqueNumberLock) {
num = nextUniqueNumber++;
}
//生成一个随机代理类名
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Verify that the class loader hasn't already defined a class
* with the chosen name.
*/

//这一句就是重中之重了,生成代理类的class文件,这就是JDK动态代理的原理了,通过动态生成class文件来产生的代理类
//这个generateProxyClass方法下面会着重介绍
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
try {
//得到class文件二进制流后,直接载入代理类
proxyClass = defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other invalid
* aspect of the arguments supplied to the proxy class
* creation (such as virtual machine limitations exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
//proxyClasses这个Map是为了来判断是不是代理的Class
// add to set of all generated proxy classes, for isProxyClass
proxyClasses.put(proxyClass, null);

} finally {
/*
* We must clean up the "pending generation" state of the proxy
* class cache entry somehow. If a proxy class was successfully
* generated, store it in the cache (with a weak reference);
* otherwise, remove the reserved entry. In all cases, notify all
* waiters on reserved entries in this cache.
*/
synchronized (cache) {
if (proxyClass != null) {
//最终将生成的代理用弱引用包装起来放到cache当中
cache.put(key, new WeakReference(proxyClass));
} else {
//如果代理类是空则移除代理的接口所代表的key值
cache.remove(key);
}
//通知正在等待在cache对象上的线程,告诉他们可以继续了,代理Class加载完毕了
cache.notifyAll();
}
}
return proxyClass;
}

上面基本上已经解释的很清楚了,下面就是去看一下byte[] proxyClassFile =ProxyGenerator.generateProxyClass(proxyName, interfaces)这句话是如何处理的,也就是如何得到的代理类的class文件的,我们进去源码看一下,我依然会加上注释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static byte[] generateProxyClass(String paramString, Class[] paramArrayOfClass) {
//新建一个ProxyGenerator实例,传入类名和接口数组
ProxyGenerator localProxyGenerator = new ProxyGenerator(paramString, paramArrayOfClass);
//这个才是真正生成class文件的地方
final byte[] arrayOfByte = localProxyGenerator.generateClassFile();
//看保存生成文件的标志是否为真,如果是就将class文件生成到本地,生成时要检查权限
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
try {
FileOutputStream localFileOutputStream = new FileOutputStream( ProxyGenerator.dotToSlash(this.val$name) + ".class");
localFileOutputStream.write(arrayOfByte);
localFileOutputStream.close();
return null;
} catch (IOException localIOException) {
throw new InternalError( "I/O exception saving generated file: " + localIOException);
}
}

});
}

return arrayOfByte;
}

我们继续跟踪到localProxyGenerator.generateClassFile()这一句当中,依然会加上注释。

1
2
3
4
5
6
7
8
9
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
private byte[] generateClassFile() {
//addProxyMethod方法,就是将方法都加入到一个列表中,并与对应的class对应起来
//这里给Object对应了三个方法hashCode,toString和equals
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);

//同样将接口与接口下的方法对应起来
for (int i = 0; i < this.interfaces.length; i++) {
localObject1 = this.interfaces[i].getMethods();
for (int k = 0; k < localObject1.length; k++) {
addProxyMethod(localObject1[k], this.interfaces[i]);
}

}
//检查所有代理方法的返回类型
for (Iterator localIterator1 = this.proxyMethods.values().iterator(); localIterator1
.hasNext();) {
localObject1 = (List) localIterator1.next();
checkReturnTypes((List) localObject1);
}

Object localObject2;
try {
//方法中加入构造方法,这个构造方法只有一个,就是一个带有InvocationHandler接口的构造方法
//这个才是真正给class文件,也就是代理类加入方法了,不过还没真正处理,只是先加进来等待循环,构造方法在class文件中的名称描述是<init>
this.methods.add(generateConstructor());
//循环代理方法
for (localIterator1 = this.proxyMethods.values().iterator(); localIterator1
.hasNext();) {
localObject1 = (List) localIterator1.next();
for (localIterator2 = ((List) localObject1).iterator(); localIterator2
.hasNext();) {
localObject2 = (ProxyMethod) localIterator2.next();
//给每一个代理方法加一个Method类型的属性,数字10是class文件的标识符,代表这些属性都是private static的
this.fields.add(new FieldInfo(((ProxyMethod) localObject2).methodFieldName,"Ljava/lang/reflect/Method;", 10));
//将每一个代理方法都加到代理类的方法中
this.methods.add(((ProxyMethod) localObject2).generateMethod());
}
}
Iterator localIterator2;
//加入一个静态初始化块,将每一个属性都初始化,这里静态代码块也叫类构造方法,其实就是名称为<clinit>的方法,所以加到方法列表
this.methods.add(generateStaticInitializer());
} catch (IOException localIOException1) {
throw new InternalError("unexpected I/O Exception");
}
//方法和属性个数都不能超过65535,包括刚才的接口个数也是这样,
//这是因为在class文件中,这些个数都是用4位16进制表示的,所以最大值是2的16次方-1
if (this.methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
}
if (this.fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
}
//这里是将类名中的.转成成斜线为了写入class文件。
this.cp.getClass(dotToSlash(this.className));
this.cp.getClass("java/lang/reflect/Proxy");
for (int j = 0; j < this.interfaces.length; j++) {
this.cp.getClass(dotToSlash(this.interfaces[j].getName()));
}

this.cp.setReadOnly();
//这里开始真正的写class文件
ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream();
Object localObject1 = new DataOutputStream(localByteArrayOutputStream);
try {
//写入class文件的标识号,标识这是一个class文件
((DataOutputStream) localObject1).writeInt(-889275714);
//次版本号0
((DataOutputStream) localObject1).writeShort(0);
//主版本号,49代表的是JDK1.5
((DataOutputStream) localObject1).writeShort(49);
//这里写入的是常量池,包括一些属性名称,类名称,方法描述符,属性描述符等等,常量池在加载时会被放到方法区或者说永久代。
this.cp.write((OutputStream) localObject1);
//这里写入的是这个类的访问标识,49代表的是public final,也就是说JDK动态代理生成的代理类都是final的
((DataOutputStream) localObject1).writeShort(49);
//写入代理类的类名
((DataOutputStream) localObject1).writeShort(this.cp
.getClass(dotToSlash(this.className)));
//写入代理类的父类类名,也就是Proxy类,这个位置的类如果说是JAVA文件,相当于extend后面的类,也就是父类
((DataOutputStream) localObject1).writeShort(this.cp
.getClass("java/lang/reflect/Proxy"));
//写入代理类所实现的接口数量
((DataOutputStream) localObject1)
.writeShort(this.interfaces.length);
//写入代理类所实现的接口类名,同样的,对于JAVA文件来说,相当于implements后面的接口,也就是实现的接口
for (int m = 0; m < this.interfaces.length; m++) {
((DataOutputStream) localObject1).writeShort(this.cp
.getClass(dotToSlash(this.interfaces[m].getName())));
}
//写入属性个数
((DataOutputStream) localObject1).writeShort(this.fields.size());
//写入属性描述
for (Iterator localIterator3 = this.fields.iterator(); localIterator3
.hasNext();) {
localObject2 = (FieldInfo) localIterator3.next();
((FieldInfo) localObject2)
.write((DataOutputStream) localObject1);
}
//写入方法个数
((DataOutputStream) localObject1).writeShort(this.methods.size());
//写入方法描述,方法的code属性,以及构造方法和类构造方法都在这里被写入了。
for (localIterator3 = this.methods.iterator(); localIterator3
.hasNext();) {
localObject2 = (MethodInfo) localIterator3.next();
((MethodInfo) localObject2)
.write((DataOutputStream) localObject1);
}
//结束
((DataOutputStream) localObject1).writeShort(0);
} catch (IOException localIOException2) {
throw new InternalError("unexpected I/O Exception");
}

return localByteArrayOutputStream.toByteArray();
}

其实代理类的class文件并不复杂,还是有很多规律可循的,所以上述过程基本上可以让各位了解下JDK动态代理生成代理类时都生成了什么东西。

下面我们可以调用下JDK中生成Class文件的方法,并且写入到本地文件,然后使用反编译工具来看一下生成的代理类到底是什么样子的。下面是生成文件的测试类。我们暂且将生成的类名写成TestProxy,代理的接口就是我们上面的TestInterface。如下。

1
2
3
4
5
6
7
8
9
10
11
12
public class CreateClassTest {

public static void main(String[] args) throws IOException {
byte[] classFile = ProxyGenerator.generateProxyClass("TestProxy", new Class[]{TestInterface.class});
File file = new File("F:/TestProxy.class");
FileOutputStream fos = new FileOutputStream(file);
fos.write(classFile);
fos.flush();
fos.close();
}

}

生成后,我们反编译过来会是如下格式的JAVA文件。我加入了注释,大致说明了下文件中生成的部分与刚才分析的时候写入的过程的对应关系。

1
2
3
4
5
6
7
8
9
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//public final的,继承Proxy,实现你传入的接口
public final class TestProxy extends Proxy
implements TestInterface
{
//private static 的Method属性,对应所有方法
private static Method m1;
private static Method m5;
private static Method m3;
private static Method m4;
private static Method m0;
private static Method m2;
//唯一的构造方法,需要一个InvocationHandler接口传入
public TestProxy(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
//重写Object的三个方法
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}

public final void method3()
throws
{
try
{
this.h.invoke(this, m5, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
//代理的三个方法,回调传入的InvocationHandler的invoke方法
public final void method1()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}

public final void method2()
throws
{
try
{
this.h.invoke(this, m4, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}

public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}

public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
//这个就是刚才this.methods.add(generateStaticInitializer());这一句话所加入的静态初始化块,初始化每一个属性
static
{
try
{//每一个属性所代表的Method都是与上面加入代理方法列表时与固定类绑定的,这是class文件中的格式,方法要与固定的类绑定
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m5 = Class.forName("TestInterface").getMethod("method3", new Class[0]);
m3 = Class.forName("TestInterface").getMethod("method1", new Class[0]);
m4 = Class.forName("TestInterface").getMethod("method2", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}

看到这里就知道invoke方法是干嘛的了,其实就是生成的代理类对每一个方法的处理就是回调invoke方法。从生成的代理类源文件中也可以发现,每一个Method除了hashCode,toString和equals外,都是与所属的接口绑定的,所以这也就解释了为什么我们不实现这个接口,只传入进入的话,不能直接使用method.invoke,而是要转成source对应的method才可以调用。

好了,代理模式就分析到这里了,这里讲的更多的是代理模式的原理,对于如何使用并没有讲述太多,是因为代理模式在平时工作中用的虽然很多,但我们大多是使用的现成的,原因很简单,就是因为spring的AOP已经给我们弄了一个很好的动态代理的框架,所以我们几乎不需要自己去写,只要明白其原理,知道动态代理和静态代理主要处理的问题是那种的,知道在何处用,也能够用起来得心应手就可以了。

-------------���Ľ�����л�����Ķ�-------------