本文侧重分析JDK17中jdk动态代理的源码,若是想看JDK8源码分析可以看我的这一篇文章 JDK8源码分析Jdk动态代理底层原理-CSDN博客
两者之间有着略微的差别,JDK17在JDK8上改进了不少
目录
JDK 17的动态代理源码
核心入口方法 newProxyInstance
获取代理类构造函数 getProxyConstructor
单接口和多接口的区别
缓存的实现和使用
性能优化总结
ProxyBuilder类的核心实现
代理类生成过程 defineProxyClass
代理类的结构
本文侧重分析JDK17中jdk动态代理的源码,若是想看JDK8源码分析可以看我的这一篇文章 JDK8源码分析Jdk动态代理底层原理-CSDN博客
两者之间有着略微的差别,JDK17在JDK8上改进了不少
JDK 17的动态代理源码
核心入口方法 newProxyInstance
@CallerSensitive
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);
}
这是创建代理实例的主要入口,他主要就是检查InvocationHandler是否为空,然后获取调用者类信息(用于安全检查),获取代理类的构造函数(这个阶段就,创建并返回代理实例
获取代理类构造函数 getProxyConstructor
private static Constructor<?> getProxyConstructor(Class<?> caller,
ClassLoader loader,
Class<?>... interfaces) {
// 优化:如果只有一个接口,使用简化的缓存查找
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()
);
} else {
// 多接口情况
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()
);
}
}
这个方法负责获取代理类的构造函数:对单接口情况做了特殊优化,还使用了使用缓存机制避免重复生成代理类,最后通过ProxyBuilder来实际构建代理类,这个ProxyBuilder是Proxy类的静态内部类。
先了解一下Proxy类采用的缓存机制和单接口优化
单接口和多接口的区别
// 单接口情况
Class<?> intf = interfaces[0];
return proxyCache.sub(intf).computeIfAbsent(...)
// 多接口情况
final Class<?>[] intfsArray = interfaces.clone();
final List<Class<?>> intfs = Arrays.asList(intfsArray);
return proxyCache.sub(intfs).computeIfAbsent(...)
内存优化:单接口直接使用接口Class对象作为缓存key,而多接口需要克隆数组并转换为List,会产生额外的对象创建
缓存key的复杂度:单接口的key就是一个Class对象,更简单高效,而多接口的key是List<Class<?>>,需要进行equals比较时要遍历所有元素
查找效率:单接口的缓存查找更快,因为只需要比较一个Class对象,而多接口需要比较整个接口列表,耗时更多
缓存的实现和使用
再回到源码中
// Proxy类中的缓存字段
private static final ClassLoaderValue<Constructor<?>> proxyCache =
new ClassLoaderValue<>();
这个就算是个map存储已经生成过的代理类。
缓存查找的过程:
// 单接口
proxyCache.sub(intf) // 使用接口Class作为key创建子缓存
.computeIfAbsent( // 在子缓存中查找或计算
loader, // 使用ClassLoader作为key
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build() // 缓存未命中时的计算逻辑
)
// 多接口
proxyCache.sub(intfs) // 使用接口List作为key创建子缓存
.computeIfAbsent(...) // 同上
首先检查缓存是否存在,如果存在,直接返回缓存的Constructor,如果不存在,则创建新的代理类,创建ProxyBuilder实例,生成代理类,获取构造函数,将构造函数存入缓存
性能优化总结
空间优化:单接口不需要创建额外的数组和List对象,缓存机制避免重复生成代理类
时间优化:单接口的缓存查找更快,避免重复的字节码生成,使用computeIfAbsent保证线程安全的同时提高并发性能
缓存命中率:相同接口组合的代理类只会生成一次,基于ClassLoader的分层缓存,避免类加载器隔离导致的缓存失效
ProxyBuilder类的核心实现
private static final class ProxyBuilder {
// 代理类名称前缀
private static final String proxyClassNamePrefix = "$Proxy";
// 用于生成唯一代理类名的计数器
private static final AtomicLong nextUniqueNumber = new AtomicLong();
private final List<Class<?>> interfaces;
private final Module module;
ProxyBuilder(ClassLoader loader, List<Class<?>> interfaces) {
// 验证模块系统是否初始化完成
if (!VM.isModuleSystemInited()) {
throw new InternalError("Proxy is not supported until "
+ "module system is fully initialized");
}
// 验证接口数量不超过限制
if (interfaces.size() > 65535) {
throw new IllegalArgumentException("interface limit exceeded: "
+ interfaces.size());
}
// 获取所有引用的类型
Set<Class<?>> refTypes = referencedTypes(loader, interfaces);
// 验证代理接口
validateProxyInterfaces(loader, interfaces, refTypes);
this.interfaces = interfaces;
// 确定代理类所属的模块
this.module = mapToModule(loader, interfaces, refTypes);
}
}
ProxyBuilder负责构建代理类,验证接口数量限制
代理类生成过程 defineProxyClass
private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
String proxyPkg = null; // 代理类的包名
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
boolean nonExported = false;
// 确定代理类的包名和访问权限
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL; // 非公共的,只需要final
String pkg = intf.getPackageName();
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
} else {
if (!intf.getModule().isExported(intf.getPackageName())) {
nonExported = true;
}
}
}
// 生成代理类名称
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg.isEmpty()
? proxyClassNamePrefix + num
: proxyPkg + "." + proxyClassNamePrefix + num;
// 生成代理类的字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
loader, proxyName, interfaces, accessFlags);
// 定义代理类
return JLA.defineClass(loader, proxyName, proxyClassFile,
null, "__dynamic_proxy__");
}
代理类生成先确定代理类的包名和访问权限,生成唯一的代理类名,再生成代理类的字节码,最后通过类加载器定义代理类
代理类的结构
public final class $Proxy0 extends Proxy implements UserService {
private static final Method m0;
private static final Method m1;
static {
try {
m0 = Class.forName("java.lang.Object")
.getMethod("hashCode");
m1 = Class.forName("com.example.UserService")
.getMethod("addUser", String.class);
} catch (NoSuchMethodException|ClassNotFoundException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
public $Proxy0(InvocationHandler h) {
super(h);
}
public final void addUser(String param0) {
try {
super.h.invoke(this, m1, new Object[]{param0});
} catch (RuntimeException|Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
JDK 17的动态代理实现相比JDK8版本的主要改进:
- 增加了模块系统支持
- 改进了缓存机制
- 增强了安全性检查
- 优化了性能
- 提供了更好的错误处理