JDK源码分析——动态代理源码(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
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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
读源码,怕过两天又忘记了,还是记录下吧..

动态代理最重要的实现就是Proxy.newInstance ,那我们直接看这个方法

public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)//这里有三个参数,第一个是传入classloader,一般情况是传入当前的classloader.但是我在上一节模拟实现里传入的是URL loader..第二个参数表示的是接口,第三个是Invocationhandler,除了第一个参数,其他和我在上一节里的一样.JDK的封装的比较好,所以他传入的是Interface的数组,
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();//如果Invocationhandler 为空,抛异常
}

/*
* Look up or generate the designated proxy class.这个方法里最主要的地方在这里,它直接通过调用 getProxyClass方法获取到了自动生成的动态代理类的二进制码.在我上一节的内容里,我们是通过自己生成JAVA文件,然后通过JAVA文件动态编译成对应的class文件,然后通过URLClassLoader.loadClass("com.cjb.proxy.Proxy1");这个方法来获取对应的二进制码的.这个方法下面会继续解释.
*/
Class cl = getProxyClass(loader, interfaces);

/*
* Invoke its constructor with the designated invocation handler.这个方法就是获取对应class的Constructor,然后通过这个Constructor来实例化..
*/
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());
}
}

上面的方法,除了最重要的getProxyClass,其他都很容易理解.,那么下面开始读getProxyClass方法

public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
throws IllegalArgumentException
{
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");//JDK想的果然比较到位,连interface传的太多都想到了..~!~
}

Class proxyClass = null;//这个就是最后要生成的二进制码,首先初始化一下

/* collect interface names to use as key for proxy class cache */
String[] interfaceNames = new String[interfaces.length];//这个存放的是对应的Interface的名字..

Set interfaceSet = new HashSet(); //这个HashSet 是为了检测interface重复记录的.

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 {
interfaceClass = Class.forName(interfaceName, false, loader);//创建对应接口的二进制码,第二个参数false表示,不需要初始化
} catch (ClassNotFoundException e) {
}
if (interfaceClass != interfaces[i]) {//如果创建出来的二进制码和原来的接口不一样,表示这个接口对于这个classloader来说是不可见的
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.前面说,InterfaceSet是拿来判断对应的interface接口是否有重复的.这里的方法是:在循环interfaces的时候,把每个interface都添加到interfaceSet里..当然,在添加之前会判断,当前循环到的接口在InterfaceSet里是否有,如果已经有了,则抛异常,说这个接口重复了..没有,则添加..
*/
if (interfaceSet.contains(interfaceClass)) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
interfaceSet.add(interfaceClass);

interfaceNames[i] = interfaceName;//这句就是把每个接口名放到interfaceNames数组里..
}

/*
* 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);//这里把Interface数组转换成list..这里直接写成了Object类型

/*
* Find or create the proxy class cache for the class loader.
*/
Map cache;//放缓存的Map
synchronized (loaderToCache) {//loaderToCache也是一个Map.它的key是classloader,对应的value是对应的缓存,也是一个HashMap.他把对应的不同的classloader放到loaderToCache里,如果下次还要调用这个方法创建代理,并传入的是同一个classloader,那么可以直接从cache里取..增加速度.当然,如果没有,则创建一条记录,放到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);//这里从cache里获取对应的Object..第一次执行的话,明显获取到的是Null..key表示的是用接口名字转换而来的list..这个可以看上面的代码.
if (value instanceof Reference) {
proxyClass = (Class) ((Reference) value).get();//如果已经能获取到了,那么,我们需要的二进制码文件就是这个获取到的
}
if (proxyClass != null) {
// proxy class already generated: return it
return proxyClass;
} else if (value == pendingGenerationMarker) {//这里的pendingGenerationMarker是一个静态常量,表示 new Object().JDK给出的 解释是,如果代理正在创建,那么等待他
// 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);//如果cache里获取到的对应于key的value是Null ,那么,就创建一个object对象放进去.上面说了,pendingGenerationMarker= new Object();
break;
}
} while (true);
}

try {
String proxyPkg = null; // package to define proxy class in 这个是代理类的包名

/*
* 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();//getModifiers()方法返回的是该接口的修饰类型,用Int类型表示
if (!Modifier.isPublic(flags)) {//如果不是public 的接口..

//我们可以这样理解,我们动态创建的代理,他的修饰类型必须是和接口的修饰类型是一样的,我们知道,接口可以是public或者默认,两种修饰类型.这里的判断如果不是public接口,那么,该接口肯定是默认的,如果是默认修饰类型,那么,它只能被同一个包下面的类看到,所以,就必须为该代理类创建一个包名..当然,如果是public的话,就没必要,因为,反正所有的类都能看到..
String name = interfaces[i].getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));//注意这里的N+1 ,其实是包括 "."的..比如 com.cjb.proxy.Proxy..它返回的就是"com.cjb.proxy."注意最后的那个点

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,这里可以看到,如果是public 的接口,对应代理类的包名就是"",也就是没有包名
proxyPkg = ""; // use the unnamed package
}

{
/*
* Choose a name for the proxy class to generate.
*/
long num;
synchronized (nextUniqueNumberLock) {
num = nextUniqueNumber++;//Num是一个计数器.用处是,创建代理的类名的时候用..我们可以看到,它是初始值是0.然后,每被调用一次,Num++.
}
String proxyName = proxyPkg + proxyClassNamePrefix + num;//proxyPkg是之前生成的包名, proxyClassNamePrefix 是一个静态常量,proxyClassNamePrefix="$Proxy".最后Num是计数器.也就是说,它的代理类的名字是从 $Proxy1 $Proxy2 $Proxy3一直在增长的,这样的话,就避免了重复.
/*
* Verify that the class loader hasn't already
* defined a class with the chosen name.
*/

/*
* Generate the specified proxy class.下面打红字的两个方法是最后生成代理对象的..但是,很悲剧的是,他是用native修饰的,也就是说,它是不是用java来实现的..也就是说,最最关键的地方,不是用java实现的...
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
try {
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());
}
}
// 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.put(key, new WeakReference(proxyClass));
} else {
cache.remove(key);
}
cache.notifyAll();
}
}
return proxyClass;
}

可以看到,我们想看的,最重要的生成二进制码的方法,是native的..读了这么多源码,都是一些前期处理.关键的地方不知道..当然,虽然没看到关键的地方,但是对于它前面的处理的学习,也是非常有用的..看看JDK是怎么处理重名问题的,怎么处理cache.等等..