Java 提供给了我们一个非常方便的动态代理类
Proxy
,让我们今天来研究一下它的实现原理,以及为什么动态代理会存在性能问题。
个人博客:
https://blog.N0tExpectErr0r.cn
小专栏:
https://xiaozhuanlan.com/N0tExpectErr0r
代理对象的创建
我们往往通过
newProxyInstance(ClassLoader, Class<?>[], InvocationHandler)
方法进行代理对象的创建,它传递了三个参数:
loader
、
interfaces
以及
h
:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException {
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
// 通过 SercurityManager 进行对即将代理的类的 PackageAccess 的权限检测
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
// 尝试获取或创建代理类
Class<?> cl = getProxyClass0(loader, intfs);
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
// 对非public类通过AccessController将Accessible设置为true
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// 以 InvocationHandler 作为参数调用代理类的构造函数
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);
}
}
这里主要是如下的几步:
-
通过
SecurityManager
对即将代理的类的 package access 进行检测。 -
通过
getProxyClass0
尝试获取代理
Class
类。 -
对非 public 的类,通过
AccessController
将
Accessible
设置为 true -
以
InvokeHandler
作为参数调用代理类的构造函数构造对象。
显然,动态代理的关键想必就是在
getProxyClass0
这个方法中了,可以大胆猜测一下这个代理类中的任何方法的调用都会通过传递进去的
InvokeHandler
进行代理。
我们看到
getProxyClass0
:
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
很奇怪,这里竟然只有一个从
proxyClassCache.get
的调用来获取缓存中的代理 Class,刚开始看到这里的时候以为
proxyClassCache
是个
Map
导致非常困惑。之后看到了上面的注释:
如果代理类已经被给定的
ClassLoader
实现过了,则从缓存中直接 copy 一份拿出,否则它会通过代理类的工厂
ProxyClassFactory
进行创建
然后再仔细一看,原来
proxyClassCache
是一个
WeakCache
对象😂,那看来对代理
Class
对象的创建就在它的
get
方法中实现了。
WeakCache 缓存
数据结构
首先我们来了解一下
WeakCache
究竟是用来干什么的,我们先看到它内部的数据结构:
final class WeakCache<K, P, V> {
private final ReferenceQueue<K> refQueue
= new ReferenceQueue<>();
// the key type is Object for supporting null key
private final ConcurrentMap<Objec