Java動(dòng)態(tài)代理的用法如下:
public class Main {
public static void main(String[] args) throws IOException {
// 1. 創(chuàng)建Proxy對(duì)象,并強(qiáng)制轉(zhuǎn)換為接口類型
Test proxy = (Test)Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]{Test.class}, new InvocationHandler() { // 2. 創(chuàng)建InvocationHandler對(duì)象,并在invoke中做方法實(shí)現(xiàn)
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(new Test() {
@Override
public void print() {
System.out.println("這里是匿名內(nèi)部類");
}
}, args);
return null;
}
});
// 3. 使用
proxy.print();
}
}
interface Test {
public abstract void print();
}
而上面InvocationHandler的invoke方法中對(duì)接口定義的方法的實(shí)現(xiàn)是通過(guò)接口的匿名內(nèi)部類完成的,當(dāng)然還可以使用其他的方式,例如:
Test proxy = (Test)Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]{Test.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("這里是代理方法");
if (method.getName().equals("print")) {
System.out.println("你調(diào)用了print方法,來(lái)自于接口");
} else {
System.out.println("你調(diào)用了來(lái)自O(shè)bject的方法,"+method.getName());
}
return null;
}
});
proxy.print();
上述展示的在InvocationHandler的invoke方法中對(duì)各個(gè)方法調(diào)用的實(shí)現(xiàn)是通過(guò)if語(yǔ)句判斷調(diào)用的方法,然后進(jìn)行操作的。
關(guān)于Java中的動(dòng)態(tài)代理,其中幾個(gè)關(guān)鍵角色說(shuō)一下:
- Proxy:官方提供的類,主要使用其newProxyInstance靜態(tài)方法獲取對(duì)接口進(jìn)行實(shí)現(xiàn)的、真正動(dòng)態(tài)代理類對(duì)象;
- InvocationHandler:方法調(diào)用委派對(duì)象,接口中所定義的API方法,在生成的動(dòng)態(tài)代理類中,全都是分派到該handler的invoke方法,即接口方法的真正實(shí)現(xiàn)邏輯是需要開(kāi)發(fā)者在handler的invoke中進(jìn)行。
說(shuō)明:在InvocationHandler的invoke方法中,第一個(gè)參數(shù)proxy就是實(shí)際的代理對(duì)象,而第二個(gè)參數(shù)為前者含有的各個(gè)方法,最后一個(gè)是方法參數(shù)。他們之間的關(guān)系如下:
class Proxy$0 { Method m1 = Class.forName("XXX.Proxy$0").getMethod("method1", Class.forName("[java.lang.Object")); public Object method1(Object[] args) { return handler.invoke(this, m1, args); } }即傳入的InvocationHandler的第一個(gè)Object參數(shù)為代理類對(duì)象this,第二個(gè)參數(shù)為被調(diào)用的方法Method對(duì)象,第三個(gè)參數(shù)是方法參數(shù)。
因此,InvocationHandler的invoke方法中,不能夠method.invoke(proxy, args),這會(huì)導(dǎo)致無(wú)限死循環(huán)proxy.h.invoke() -> proxy.method.invoke() -> proxy.h.invoke() ...
因此,根據(jù)上述的使用方式,我們有如下兩個(gè)疑問(wèn):
- 生成的代理類如何持有InvocationHandler對(duì)象的
- 生成的代理類如何實(shí)現(xiàn)方法的
Proxy通過(guò)一個(gè)靜態(tài)方法newProxyInstance隱去了所有的細(xì)節(jié),我們就來(lái)看看該方法的實(shí)現(xiàn):
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
Objects.requireNonNull(h);
final Class<?> caller = System.getSecurityManager() == null
? null
: Reflection.getCallerClass();
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
return newProxyInstance(caller, cons, h);
}
該方法,大體上很簡(jiǎn)單,獲取構(gòu)造函數(shù),然后調(diào)用newProxyInstance方法返回對(duì)象,而newProxyInstance方法內(nèi)部也只是簡(jiǎn)單的回調(diào)cons.newInstance:
private static Object newProxyInstance(Class<?> caller, // null if no SecurityManager
Constructor<?> cons,
InvocationHandler h) {
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (caller != null) {
checkNewProxyPermission(caller, cons.getDeclaringClass());
}
return cons.newInstance(new Object[]{h});
} ....
}
唉,我們發(fā)現(xiàn),調(diào)用Constructor的newInstance時(shí),傳遞的參數(shù)正式InvocationHandler對(duì)象,這就說(shuō)明了,生成的代理類有一個(gè)構(gòu)造器,該構(gòu)造器接收一個(gè)InvocationHandler對(duì)象為參數(shù)。因此,就來(lái)到了第一個(gè)關(guān)注的問(wèn)題:代理類是如何以及何時(shí)生成這樣一個(gè)構(gòu)造器的?問(wèn)題的答案,一定來(lái)自getProxyConstructor方法,在分析該方法前我們先不關(guān)心這里的參數(shù)caller為何,僅當(dāng)其為null。
private static Constructor<?> getProxyConstructor(Class<?> caller,
ClassLoader loader,
Class<?>... interfaces)
{
// optimization for single interface
if (interfaces.length == 1) {
Class<?> intf = interfaces[0];
if (caller != null) {
checkProxyAccess(caller, loader, intf);
}
return proxyCache.sub(intf).computeIfAbsent(
loader,
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build() // build()方法返回的才是Constructor對(duì)象
);
} else {
// interfaces cloned
final Class<?>[] intfsArray = interfaces.clone();
if (caller != null) {
checkProxyAccess(caller, loader, intfsArray);
}
final List<Class<?>> intfs = Arrays.asList(intfsArray);
return proxyCache.sub(intfs).computeIfAbsent(
loader,
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build() // build()方法返回的才是Constructor對(duì)象
);
}
}
實(shí)際上的構(gòu)造器正是通過(guò)ProxyBuilder的build()方法返回的,至于這里的getProxyConstructor方法其他部分到底是做什么的,我們先不管,直接來(lái)看ProxyBuilder.build()方法:
// ProxyBuilder.java
Constructor<?> build() {
Class<?> proxyClass = defineProxyClass(module, interfaces);
final Constructor<?> cons;
try {
cons = proxyClass.getConstructor(constructorParams);
} ...
return cons;
}
該方法也很簡(jiǎn)單明了,就是創(chuàng)建代理類的Class對(duì)象,然后獲取其構(gòu)造器返回就行了??吹竭@里是不是激動(dòng)了起來(lái),代理類的Class對(duì)象?好家伙,我們明明沒(méi)有寫(xiě)過(guò)一行代理類的源代碼,Java是怎么給我們憑空創(chuàng)建出來(lái)一個(gè)Class對(duì)象的?那么這個(gè)代理類的Class文件長(zhǎng)啥樣呢?咋生成的呢?
關(guān)于上面的代碼,還有一個(gè)要說(shuō)的點(diǎn)是constructorParams是一個(gè)在定義時(shí)就被初始化了的屬性:
private static final Class<?>[] constructorParams =
{ InvocationHandler.class };
嗯,不出所料,正印證了前面在調(diào)用構(gòu)造器的newInstance方法時(shí)需要傳入InvocationHandler對(duì)象。
這就引入了問(wèn)題的關(guān)鍵:Java是如何憑空定義一個(gè)代理類的?又是如何生成其Class文件的?搞清楚其中內(nèi)容,自然也就知道了接口中的方法以及構(gòu)造器是如何寫(xiě)的了。因此,關(guān)鍵來(lái)到了defineProxyClass方法:
// ProxyBuilder.java
private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
....
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = PROXY_GENERATOR_V49
? ProxyGenerator_v49.generateProxyClass(proxyName, interfaces, accessFlags)
: ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags);
try {
Class<?> pc = JLA.defineClass(loader, proxyName, proxyClassFile,
null, "__dynamic_proxy__");
reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
return pc;
}
}
該方法先會(huì)處理一些module以及package的信息,但這不是我們所關(guān)心的,直接來(lái)到generateProxyClass方法調(diào)用處,該方法就是生成代理類的地方,我們這里就看第二個(gè)ProxyGenerator吧。
// ProxyGenerator.java
static byte[] generateProxyClass(ClassLoader loader,
final String name,
List<Class<?>> interfaces,
int accessFlags) {
ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags);
final byte[] classFile = gen.generateClassFile();
....
return classFile;
}
已經(jīng)離答案越來(lái)越近了,生成Class文件的地方就是generateClassFile方法:
// ProxyGenerator.java
private byte[] generateClassFile() {
visit(V14, accessFlags, dotToSlash(className), null,
JLR_PROXY, typeNames(interfaces));
// 添加上hashCode、equals、toString三個(gè)從Object繼承來(lái)的方法
addProxyMethod(hashCodeMethod);
addProxyMethod(equalsMethod);
addProxyMethod(toStringMethod);
for (Class<?> intf : interfaces) {
for (Method m : intf.getMethods()) {
if (!Modifier.isStatic(m.getModifiers())) {
// 添加上所有想要代理的接口的方法
addProxyMethod(m, intf);
}
}
}
...
// 憑空生成構(gòu)造器
generateConstructor();
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// add static field for the Method object
visitField(Modifier.PRIVATE | Modifier.STATIC, pm.methodFieldName,
LJLR_METHOD, null, null);
// 為所有上面添加的方法生成方法實(shí)現(xiàn)code
pm.generateMethod(this, className);
}
}
// 生成初始化塊兒
generateStaticInitializer();
return toByteArray();
}
可以看到,generateClassFile思路非常清晰:
- 添加所有涉及到的方法,并之后generate出方法實(shí)現(xiàn);
- 為構(gòu)造器generate出實(shí)現(xiàn);
- generate靜態(tài)初始化塊兒。
這三個(gè)內(nèi)容唯獨(dú)對(duì)方法的處理需要先add,我們就先看看add的到底是啥:
private final static ProxyMethod hashCodeMethod;
private final static ProxyMethod equalsMethod;
private final static ProxyMethod toStringMethod;
static {
try {
hashCodeMethod = new ProxyMethod(Object.class.getMethod("hashCode"), "m0");
equalsMethod = new ProxyMethod(Object.class.getMethod("equals", Object.class), "m1");
toStringMethod = new ProxyMethod(Object.class.getMethod("toString"), "m2");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
可以看到,原來(lái)在ProxyGenerator中每個(gè)方法使用一個(gè)ProxyMethod指代,ProxyMethod中首先是包含了對(duì)應(yīng)的真實(shí)的Method對(duì)象,然后緊跟的一個(gè)字符串代表著該方法在代理類對(duì)象中對(duì)應(yīng)的引用的名字,例如在代理類中m0屬性如下定義:
Method m0 = Class.forName("java.lang.Object").getMethod("hashCode");
找到了地方,我們先來(lái)看看是如何為代理類生成構(gòu)造器的代碼的。
生成構(gòu)造器
該功能由generateConstructor()方法實(shí)現(xiàn):
// ProxyGenerator.java
private void generateConstructor() {
MethodVisitor ctor = visitMethod(Modifier.PUBLIC, NAME_CTOR,
MJLR_INVOCATIONHANDLER, null, null);
ctor.visitParameter(null, 0);
ctor.visitCode();
ctor.visitVarInsn(ALOAD, 0);
ctor.visitVarInsn(ALOAD, 1);
ctor.visitMethodInsn(INVOKESPECIAL, JLR_PROXY, NAME_CTOR,
MJLR_INVOCATIONHANDLER, false);
ctor.visitInsn(RETURN);
// Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
ctor.visitMaxs(-1, -1);
ctor.visitEnd();
}
一頭霧水,這些都是啥啊。不急,我們先來(lái)看看visitMethod方法傳遞的參數(shù)就知道了:
- Modifier.PUBLIC:唉,這不是public修飾符嗎?
- NAME_CTOR:是個(gè)常量,內(nèi)容為"<init>",莫名熟悉;
- MJLR_INVOCATIONHANDLER:也是個(gè)常量,內(nèi)容為"(Ljava/lang/reflect/InvocationHandler;)V"!!
看到上面的三個(gè)參數(shù),大師,我悟了?。∵@三個(gè)拼起來(lái),不就是class文件中構(gòu)造器的descriper嗎?原來(lái),Java的動(dòng)態(tài)代理真的想跳過(guò)源碼編譯的步驟,直接寫(xiě)出一個(gè)class文件來(lái)?。?/p>
看到這里大致明白了,這里的MethodVisitor應(yīng)該是代表著要往class文件中寫(xiě)入一個(gè)Method,而創(chuàng)建MethodVisitor時(shí)根據(jù)傳入的參數(shù)確定下了該Method的描述符以及訪問(wèn)權(quán)限,那么還有一個(gè)重頭戲就是Method的code部分怎么寫(xiě),這些就是上述的各種visitXXX了:
實(shí)際上根據(jù)上述幾個(gè)visitXXX的第一個(gè)參數(shù):ALOAD、INVOKESPECIAL、RETURN就能猜出來(lái)了,這寫(xiě)顯然就是JVM標(biāo)準(zhǔn)定義的棧指令描述符啊,因此,后續(xù)調(diào)用visitEnd()方法之前的各種visitXXX方法就是在硬寫(xiě)一個(gè)個(gè)指令完成方法code的書(shū)寫(xiě)。
等等,說(shuō)著說(shuō)著,怎么為代理類生成的構(gòu)造器中使用invokespecial指令回調(diào)了其他類的方法,我們先來(lái)看看回調(diào)的是誰(shuí)的方法:
JLR_PROXY -> private static final String JLR_PROXY = "java/lang/reflect/Proxy";
NAME_CTOR -> private static final String NAME_CTOR = "<init>;
MJLR_INVOCATIONHANDLER -> private static final String MJLR_INVOCATIONHANDLER = "(Ljava/lang/reflect/InvocationHandler;)V";
破案了,原來(lái)生成的代理類繼承自Proxy類,而且構(gòu)造器的實(shí)現(xiàn)就是簡(jiǎn)單的super(InvocationHandler),而我們看看Proxy構(gòu)造器:
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
好小子,分析了半天,讓你Proxy小子給截胡了。
生成普通方法
代理類中方法屬性的命名問(wèn)題
在ProxyGenerator類中直接為hashCode、toString、equals三個(gè)方法創(chuàng)建好了對(duì)應(yīng)的ProxyMethod對(duì)象,并且提到了為其在代理類中的屬性命名為了m0、m1、m2;而其他來(lái)自接口中的方法又是如何命名的呢?而上面顯示,來(lái)自于接口的方法被addProxyMethod的方式添加了進(jìn)來(lái):
// ProxyGenerator.java
private int proxyMethodCount = 3;
private void addProxyMethod(Method m, Class<?> fromClass) {
...
List<ProxyMethod> sigmethods = proxyMethods.computeIfAbsent(sig,
(f) -> new ArrayList<>(3));
...
sigmethods.add(new ProxyMethod(m, sig, m.getParameterTypes(), returnType,
exceptionTypes, fromClass,
"m" + proxyMethodCount++));
}
很簡(jiǎn)單,就是proxyMethodCount開(kāi)始計(jì)數(shù)罷了。
生成接口方法的code
在generateClassFile方法中,生成接口方法code的時(shí)機(jī)發(fā)生在生成Constructor之后,代碼如下:
// ProxyGenerator.java
private byte[] generateClassFile() {
....
// 憑空生成構(gòu)造器
generateConstructor();
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// add static field for the Method object
visitField(Modifier.PRIVATE | Modifier.STATIC, pm.methodFieldName,
LJLR_METHOD, null, null);
// 為所有上面添加的方法生成方法實(shí)現(xiàn)code
pm.generateMethod(this, className);
}
}
return toByteArray();
}
不同于generateConstructor,generateMethod定義在ProxyMethod中:
// ProxyMethod.java
private void generateMethod(ClassWriter cw, String className) {
....
MethodVisitor mv = cw.visitMethod(accessFlags,
method.getName(), desc, null,
typeNames(Arrays.asList(exceptionTypes)));
....
// 開(kāi)始編寫(xiě)code部分
mv.visitCode();
...
// 準(zhǔn)備第一個(gè)參數(shù)this
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, JLR_PROXY, handlerFieldName,
LJLR_INVOCATION_HANDLER);
// 準(zhǔn)備第二個(gè)參數(shù)
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETSTATIC, dotToSlash(className), methodFieldName,
LJLR_METHOD);
....
// invokeinterface調(diào)用方法
mv.visitMethodInsn(INVOKEINTERFACE, JLR_INVOCATION_HANDLER,
"invoke",
"(Ljava/lang/Object;Ljava/lang/reflect/Method;" +
"[Ljava/lang/Object;)Ljava/lang/Object;", true);
...
mv.visitEnd();
}
該方法的結(jié)構(gòu)和generateConstructor一致,這里的MethodVisitor肯定也可以看做方法的descripter+accessFlags,重點(diǎn)解釋如下幾個(gè)棧指令調(diào)用:
mv.visitFieldInsn(GETFIELD, JLR_PROXY, handlerFieldName,LJLR_INVOCATION_HANDLER);
JIR_PROXY -> private static final String JLR_PROXY = "java/lang/reflect/Proxy";
handlerFieldName -> private static final String handlerFieldName = "h";
LJLR_INVOCATION_HANDLER -> private static final String LJLR_INVOCATION_HANDLER = "Ljava/lang/reflect/InvocationHandler;"
mv.visitFieldInsn(GETSTATIC, dotToSlash(className), methodFieldName,LJLR_METHOD);
methodFieldName -> "m0""m1"這種
LJLR_METHOD -> private static final String LJLR_METHOD = "Ljava/lang/reflect/Method;"
// 接下來(lái)這個(gè)想必已經(jīng)不用解釋了
mv.visitMethodInsn(INVOKEINTERFACE, JLR_INVOCATION_HANDLER,
"invoke",
"(Ljava/lang/Object;Ljava/lang/reflect/Method;" +
"[Ljava/lang/Object;)Ljava/lang/Object;", true);
很顯然,在生成的代理類中,所有的方法實(shí)現(xiàn)都是如下形式:
h.invoke(this, m0, args);
即,全都委托給了代理類持有的InvocationHandler h的invoke方法來(lái)完成。
動(dòng)態(tài)代理總結(jié)
使用
動(dòng)態(tài)代理的使用最重要的就是InvocationHandler對(duì)象,該對(duì)象的invoke方法負(fù)責(zé)對(duì)代理類中所有方法進(jìn)行實(shí)現(xiàn),至關(guān)重要。
知識(shí)點(diǎn)
- 生成的代理類是Proxy這個(gè)類的子類,而后者的構(gòu)造器在代理類中被回調(diào),用于接收InvocationHandler對(duì)象;
- 代理類的所有方法都會(huì)在類中定義一個(gè)Method屬性,名字從m0開(kāi)始沿用;
- 代理類中所有方法實(shí)現(xiàn)都是如下模板:
class Proxy$0 {
protected InvocationHandler h;
private Method m3 = Class.forName("代理類的全限定名").getMethod("方法名", Class.forName("[java.lang.Objec或者接口全限定名"));
public Object method1(Object[] args) {
return h.invoke(this, m3, args);
}
}
實(shí)例
最后我們來(lái)看看現(xiàn)象,以下實(shí)例來(lái)自文章https://mp.weixin.qq.com/s/gnj8x4bSQoNRMcRUWS6p5Q:
public finalclass $Proxy0 extends Proxy implements Person { ★
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws { ②
super(var1);
}
public final boolean equals(Object var1) throws { ④
return (Boolean) super.h.invoke(this, m1, new Object[]{var1});
}
public final void rent() throws { ③
super.h.invoke(this, m3, (Object[]) null);
}
public final String toString() throws { ④
return (String) super.h.invoke(this, m2, (Object[]) null);
}
public final int hashCode() throws { ④
return (Integer) super.h.invoke(this, m0, (Object[]) null);
}
static { ①
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.dujc.mybatis.proxy.Person").getMethod("rent");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
}
}
這就是將動(dòng)態(tài)代理生成的class文件反編譯出來(lái)的內(nèi)容,可以看到,和我們上文分析的一樣。