我们知道,JDK 动态代理可以对接口进行代理,把接口的所有方法代理到 InvocationHandler 的 invoke 方法上。
示例如下:
1
2
3
4
5
package com.youthlin.example.proxy.service;
public interface IUserService {
String getName ();
}
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
package com.youthlin.example.proxy;
import com.youthlin.example.proxy.service.IUserService;
import sun.misc.ProxyGenerator;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 创建: youthlin.chen
* 时间: 2017-11-28 11:49
*/
public class Main {
public static void main (String[] args) throws IOException {
IUserService userService = (IUserService) Proxy.newProxyInstance (
IUserService.class.getClassLoader (),
new Class[]{IUserService.class},
new InvocationHandler () {
@ Override
public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
return "YouthLin" ;
}
});
System.out.println("不同包接口 " + userService.getName ());
//writeProxyClass(userService.getClass());
}
}
这样我们就可以对一个 没有实现 的接口进行调用了,实际的调用代理给了
IncovationHandler 实例的
invoke 方法。那么这个是怎么实现的呢?
我们可以使用
sun.misc.ProxyGenerator#generateProxyClass(java.lang.String, java.lang.Class<?>[]) 这个方法获取到生成的代理类的字节码数组,把字节码输出为一个 .class 文件,在 IDEA 中就可以看到源码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private static void writeProxyClass(Class<?> proxyClass) throws IOException {
String proxyClassName = proxyClass.getName();
//System.writeProxyClass.println(proxyClassName);
proxyClassName = proxyClassName.substring(proxyClassName.lastIndexOf(".") + 1);
//System.writeProxyClass.println(proxyClassName);
byte[] proxyClassBytes = ProxyGenerator.generateProxyClass(proxyClassName, new Class[]{IHelloService.class});
String path = Main.class.getResource("/").getPath() + "/generate/";
File file = new File(path, proxyClass.getName().replaceAll("\\.", "/") + ".class");
if (!file.getParentFile().exists()) {
//noinspection ResultOfMethodCallIgnored
file.getParentFile().mkdirs();
}
System.out.println(proxyClass.getName() + " " + file.getAbsolutePath());
FileOutputStream out = new FileOutputStream(file);
out.write(proxyClassBytes);
out.flush();
out.close();
}
然后在 main 方法里调用 writeProxyClass(userService.getClass()); 就可以了。用 IDEA 打开输出文件,可以看到:
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
/ /
/ / Source code recreated from a . class file by IntelliJ IDEA
/ / (powered by Fernflower decompiler)
/ /
import com. youthlin. example. proxy. Main. IHelloService;
import java. lang. reflect. InvocationHandler;
import java. lang. reflect. Method;
import java. lang. reflect. Proxy;
import java. lang. reflect. UndeclaredThrowableException;
public final class $ Proxy1 extends Proxy implements IHelloService {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $ Proxy1(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super. h. invoke(this, m1, new Object []{var1})). booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String sayHello(String var1) throws {
try {
return (String )super. h. invoke(this, m3, new Object []{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String )super. h. invoke(this, m2, (Object [])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return ((Integer)super. h. invoke(this, m0, (Object [])null)). intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class. forName(" java.lang.Object " ). getMethod(" equals " , Class. forName(" java.lang.Object " ));
m3 = Class. forName(" com.youthlin.example.proxy.Main$IHelloService " ). getMethod(" sayHello " , Class. forName(" java.lang.String " ));
m2 = Class. forName(" java.lang.Object " ). getMethod(" toString " );
m0 = Class. forName(" java.lang.Object " ). getMethod(" hashCode " );
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2. getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3. getMessage());
}
}
}
也就是说,生成动态代理对象时,实际上是 JDK 动态编译了一个类,继承了
Proxy 类,实现了传入的接口类。然后把接口的方法(及 Object 的三个实例方法 toString, hashCode, equals)都代理给了父类 Proxy 的
InvocationHandler 实例。所以我们可以在自定义的 InvocationHandler 的 invoke 方法中进行处理。
Proxy
java.lang.reflect.Proxy#newProxyInstance
这个方法用于生成代理对象,里面比较重要的就是
Class<?> cl = getProxyClass0(loader, intfs); 这一句,拿到代理类的 Class 实例,之后
return cons.newInstance(new Object[]{h}); 即可生成代理对象。
java.lang.reflect.Proxy#getProxyClass0
这个方法实现很简单:
return proxyClassCache.get(loader, interfaces); 从一个
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); 缓存中获取类实例。其实真正的获取在这个地方:
java.lang.reflect.Proxy.ProxyClassFactory#apply
首先一些权限检查之类的,略过,拿到代理对象的类名,最终也落在
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); 这个调用上。最后通过本地方法将 byte[] 转化为 Class 对象就完成了。
ProxyGenerator
sun.misc.ProxyGenerator#generateProxyClass(java.lang.String, java.lang.Class<?>[], int)
sun.misc.ProxyGenerator#generateClassFile
这里就是动态生成字节码的地方了。
其实是根据 JVM 规范,把类信息写入了字节数组
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
try {
var14. writeInt(- 889275714 );
var14. writeShort(0 );
var14. writeShort(49 );
this. cp. write(var14);
var14. writeShort(this. accessFlags);
var14. writeShort(this. cp. getClass(dotToSlash(this. className)));
var14. writeShort(this. cp. getClass(" java/lang/reflect/Proxy " ));
var14. writeShort(this. interfaces. length);
Class[] var17 = this. interfaces;
int var18 = var17. length;
for (int var19 = 0 ; var19 < var18; + + var19) {
Class var22 = var17[var19];
var14. writeShort(this. cp. getClass(dotToSlash(var22. getName())));
}
var14. writeShort(this. fields. size());
var15 = this. fields. iterator();
while (var15. hasNext()) {
ProxyGenerator. FieldInfo var20 = (ProxyGenerator. FieldInfo)var15. next();
var20. write(var14);
}
var14. writeShort(this. methods. size());
var15 = this. methods. iterator();
while (var15. hasNext()) {
ProxyGenerator. MethodInfo var21 = (ProxyGenerator. MethodInfo)var15. next();
var21. write(var14);
}
var14. writeShort(0 );
return var13. toByteArray();
} catch (IOException var9) {
throw new InternalError(" unexpected I/O Exception " , var9);
}
对应下面的 Class 规范:
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count- 1 ];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
JDKProxy
本文代码可以在这里找到:
https://github.com/YouthLin/examples/tree/master/example-jdk-proxy
Reader Echoes
1 comment