本文主要讲解下JDK动态代理的实现原理,其基本使用如下:
// 实例化自定义调用处理器实例
InvocationHandler handler = new MyInvocationHandler(...);
// 获取代理对象方式一
Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);
Foo f = (Foo) proxyClass.getConstructor(InvocationHandler.class).
newInstance(handler);
// 获取代理对象方式二
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class<?>[] { Foo.class },
handler);
可以看出,只要给出要实现类的加载器、接口列表就能够在运行时创建出代理类。代理类的实例化需要传入自定义的调用处理器实例,具体代理的内容均在处理器实例中体现。
JDK动态代理在使用上十分简单,但内部具体实现逻辑有很多细节指的我们探索。下文将先从目标代理类入手,分析其结构和调用关系。然后分析Proxy中探索生成代理类的过程。
代理类是运行时生成的,一般不会产生具体的类文件。如果想要获取代理类,可设置如下系统参数:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
不同JDK版本可能参数会有所不同,参考ProxyGenerator#saveGeneratedFiles指定的Key值。
该值设置为true后,程序生成代理类是就会将对应的类文件路径为:{项目根目录}/com/sun/proxy/$proxy{index}.class,其中index为代理类的唯一序号。,
下面是为简单的Person接口生成的代理类:
public interface Person {
void ageNum();
}
public final class $Proxy0 extends Proxy implements Person {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, 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 void ageNum() throws {
try {
super.h.invoke(this, m3, (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);
} 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"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.design.代理模式.jdkDynamic.Person").getMethod("ageNum");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
可以看到代理类使用final修饰不允许被继承,访问权限是否为public由其继承的接口Person的权限决定。
代理类继承了Proxy类,并实现了指定接口的所有方法。是否正是因为代理类需继承Proxy类,且Java不允许多继承,所以代理类就只能传入对应的接口呢?为什么代理类必须得继承Proxy类?
代理类仅提供了一个构造方法,并且需传入InvocationHandler实例。InvocationHandler就是具体方法调用时的代理处理器。这个后续会说到,先有个印象。【InvocationHandler实例必传】
代理类中的成员变量均为静态的方法句柄(java.lang.reflect.Method),代理类在加载时,会通过反射的方法初始化各个Method。
Calss.forName(String className).getMethod(String methodName, ...parameterTypes)
代理类会实现接口的多个方法,并且都使用final关键字修饰,不允许被重写(本来代理类也不能被继承),访问权限依据接口中方法的访问权限决定。
我们从上面例子可以看到,代理类不仅实现了接口的方法,同时也实现了Object类hashCode()、equals()、toString()。这就说明了代理类不仅会代理指定接口的方法,默认也会代理Object类中公开的、非本地的方法。
try {
// super.h 就是 代理类构造方法传入的InvocationHandler实例
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
在方法内部执行逻辑,我们可以发现,代理类执行某个方法时,会调用InvocationHandler实例的invoke方法。
我们在前面代理类的分析中发现,代理类执行任何一个方法,实际上都会通过InvocationHandler实例的invoke方法进行处理。每一个代理实例都必须关联一个InvocationHandler实例。
InvocationHandler是java.lang.reflect包下提供的接口,该接口仅声明了一个方法:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
任何代理类调用方法时,都会调用其关联的InvocationHandler实例的invoke方法获取方法返回结果。对于其方法参数说明如下:
Proxy是JDK动态代理的核心类,主要负责代理类的生成、实例化任务。
public class Proxy implements java.io.Serializable {
}
从类声明上看,Proxy实现Serializable接口表明代理类(继承Proxy)是允许被序列化&反序列化的。
Proxy类的访问权限为public且没有声明final关键字。即便如此,一般情况下也没有人手动继承Proxy类,因为Proxy要么是静态方法,要么是私有方法,意即,单独继承Proxy几乎没有意义。
Proxy提供了两个构造方法。
private Proxy() {
}
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
Proxy的无参构造的访问权限为私有,这就意味着Proxy是不允许用户直接实例化的。实际上,Proxy直接实例化也没有意义,公开方法均为静态方法。
Proxy的有参构造访问权限为protected,是提供给子类用的,比如生成的代理类是Proxy的子类,当代理类实例化时,就会通过这个构造方法将调用处理器实例存储在Proxy#属性中。
Proxy类中有三个属性,其中2个私有类型,1个受保护类型。
内部类的内容是Proxy原理的重点,阅读本节之前必须先看懂【WeakCache二级缓存】
Proxy类中定义了5个私有静态内部类,并且均使用final关键字修饰。
KeyFactory实例作为WeakCache实例化时的第一个工厂函数对象subKeyFactory,主要用于代理类缓存的二级Key,即sub-Key的生成。
private static final class KeyFactory implements BiFunction<ClassLoader, Class<?>[], Object> {
@Override
public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
switch (interfaces.length) {
case 1: return new Key1(interfaces[0]); // the most frequent
case 2: return new Key2(interfaces[0], interfaces[1]);
case 0: return key0;
default: return new KeyX(interfaces);
}
}
}
KeyFactory类实现了BiFunction的apply方法。BiFunction(Binary Function)相比于Function接口,BiFunction会根据两个参数返回结果的函数式接口。KeyFactory其实就对应着WeakCache的subKeyFactory,因此通过KeyFactory的泛型参数可以看出代理类缓存的一级Key就是ClassLoader类型的类加载器,生成二级Key的另外一个参数就是Class>[]类型的类信息数组(实际上,这里其实是接口类信息数组,后面会说明)。
KeyFactory#apply方法生成subKey需要传入两个参数:类加载器及接口信息列表。而在实际逻辑中,可以看出KeyFactory在生成subKey的时候并没有考虑类加载器。因此代理类缓存的结构可记为<类加载器,接口信息,代理类>,也就意味着代理类在同一类加载器下同样的接口信息下是唯一的。
那具体是如何根据接口类信息数组生成二级Key呢?从KeyFactory#apply方法可以看到,二级Key实际上就是Proxy提供内部类Key[1,2,X]对象。这里是根据接口的长度来返回不同类型的对象。
这里有两个问题暂时还没搞懂:
- Key1、Key2、KeyX的区别在哪里?内存占用和性能上似乎没有区别,都是用KeyX不行吗?
- KeyX为什么不继承弱引用呢?虽然subKey即便不是弱引用也无所谓,WeakCache都没要求。
KeyX对象只会被用于WeakCache
ProxyClassFactory实例作为WeakCache实例化时的第二个工厂函数对象valueFactory,主要用于生成代理类Class。
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
...
}
ProxyClassFactory同样也是实现了BiFunction接口,实现的apply方法会根据类加载器和类信息数组生成代理类Class并返回。
先来看ProxyClassFactory内部类的两个私有类成员变量:
// 所有代理类名称前缀
private static final String proxyClassNamePrefix = "$Proxy";
// 下一个要生成的代理类序号
private static final AtomicLong nextUniqueNumber = new AtomicLong();
这两个成员变量就是用于拼接生成代理类名的,即proxyClassNamePrefix + nextUniqueNumber,比如第一章节给出的“$Proxy0”就是生成的第一个代理类名。
ProxyClassFactory的核心还是用于生成代理类Class的apply方法,代码及注释:
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
// 1. 第一部分是类信息数组的校验和验证部分
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
// 1.1 验证所有类信息都能够通过当前类加载器加载
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {}
if (interfaceClass != intf) {
throw new IllegalArgumentException(intf + " is not visible from class loader");
}
// 1.2 验证所有类信息均为接口类型【JDK动态代理只能代理接口】
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(interfaceClass.getName() + " is not an interface");
}
// 1.3 验证接口信息数组不存在重复
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // 定义代理类所在的包路径
int accessFlags = Modifier.PUBLIC | Modifier.FINAL; // 默认代理类为public final
/*
* 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.
*/
// 2. 如果接口数组中存在非public,那代理类也是非public权限。
// 并且,代理类所在的包路径为非public接口所在路径【多个非public接口需在同一个包路径下】
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.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) {
// 若接口均为public权限,默认代理类包路径为com.sun.proxy
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
// 3. 拼接出代理类的全限定名
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* 4. 根据代理类名、实现的接口、代理类权限信息生成代理类字节数组
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
// 调用本地方法,使用指定的类加载器加载代理类Class对象
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
代码十分简单,几乎就是验证了一些代理类信息正确性,确定代理类包路径、类名、访问权限等信息,最重要的是通过ProxyGenerator的静态方法来生成代理类字节码数组,并调用本地方法加载并返回代理类Class对象。
Proxy类提供了四个静态公开方法。
四个方法内部逻辑均十分简单清晰,这里仅给出常用newProxyInstance方法代码及注释。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
// 调用处理器必须不能为null
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* 根据类加载器、要实现的接口列表获取或加载代理类Class对象
*/
Class<?> cl = getProxyClass0(loader, intfs);
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// 反射找到代理类的构造方法,包含调用处理器参数
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) { // 如果当前代理类非public【接口存在非public】
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true); // 设置构造方法可见
return null;
}
});
}
return cons.newInstance(new Object[]{h}); // 反射实例化代理类对象
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
可以看到,如果前面一大部分能够理解的话,Proxy提供的四个公开静态方法实际上理解起来十分简单,这里也不做过多赘述【重点还是二级缓存&代理类的生成】。另外,还有个细节就是所有的公开方法均使用了@CallerSensitive注解,深入了解可参考@CallerSensitive一些理解。