JDK动态代理原理
@author:Jingdai
@date:2021.05.03
接上篇,记录一些学习JDK动态代理的知识点。
1.JVM生成的动态代理类都是Proxy的子类
Java8 API原句:
Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.
2.Proxy类的构造方法
Proxy类的部分源码:
/**
* the invocation handler for this proxy instance.
* @serial
*/
protected InvocationHandler h;
/**
* Prohibits instantiation.
*/
private Proxy() {
}
/**
* Constructs a new {@code Proxy} instance from a subclass
* (typically, a dynamic proxy class) with the specified value
* for its invocation handler.
*
* @param h the invocation handler for this proxy instance
*
* @throws NullPointerException if the given invocation handler, {@code h},
* is {@code null}.
*/
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
可以看出Proxy类的无参构造方法是私有的,而第一点说了所有的动态代理类都是Proxy类的子类,而子类的构造方法调用时,会自动的调用父类的构造方法。如果不指定父类的构造方法,会自动调用super(),即父类的无参构造,而这里的无参构造是私有的,不能调用,所以子类的构造方法第一行必定是super(h); 即调用Proxy类中的第二个构造方法,这就保证了所有的动态代理类的InvocationHandler h属性都是有值的。
3.调用getProxyClass()方法不一定会创建新的代理类
调用Proxy的这个静态方法时,不一定会创建一个新的class对象,如果类加载器已经为相同排列的接口创建了代理类,那就会返回现有的代理类;否则才会重新创建新的代理类。
Java8 API原句:
Returns the java.lang.Class object for a proxy class given a class loader and an array of interfaces. The proxy class will be defined by the specified class loader and will implement all of the supplied interfaces. If any of the given interfaces is non-public, the proxy class will be non-public. If a proxy class for the same permutation of interfaces has already been defined by the class loader, then the existing proxy class will be returned; otherwise, a proxy class for those interfaces will be generated dynamically and defined by the class loader.
4.为什么通过代理类调用会执行InvocationHandler的invoke()方法?
利用ProxyGenerator类的generateProxyClass方法生成代理类的字节数组,转成输出流到文件,再利用jd-gui工具反编译对应的字节码,可以得到下面代理类的代码。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Subject;
public final class $Proxy0 extends Proxy implements Subject {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject) {
try {
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void doRequest() {
try {
this.h.invoke(this, m3, null);
return;
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return ((Integer)this.h.invoke(this, m0, null)).intValue();
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("proxy.Subject").getMethod("doRequest", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
} catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
} catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}
}
接口的代码上篇文章有,再写一遍:
package proxy;
// interface
public interface Subject {
void doRequest();
}
从上面反编译的代码可以看出,调用代理类的方法doRequest()时,其实它就是在调用 this.h.invoke(this, m3, null); 方法,其中 h 就是在创建代理类时传入的InvocationHandler对象,而m3就是静态代码块中初始化的Method静态变量(通过反射得到)。这也就解释了为什么调用代理类的方法会转到InvocationHandler中去执行。同时从反编译结果可以看出,执行代理类的equals()、toString()和hashCode()方法时,也会调用invoke()方法。
5.JDK动态代理只支持接口的代理
由第4点可以看到,代理类继承自Proxy,而代理类要不实现目标类的接口,要不是目标类的子类,而这里已经继承了Proxy,所以不能继承其它类了,即JDK动态代理不能实现类的代理。