Skip to content

JDK 代理源码解析

约 1339 字大约 4 分钟

2025-12-26

代理类对象的创建:

// 1. 创建目标对象
UserService target = new UserServiceImpl();

// 2. 创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
	target.getClass().getClassLoader(),           // 类加载器
	target.getClass().getInterfaces(),            // 目标对象实现的接口
	new LogInvocationHandler(target)              // 代理处理器
);

通过Proxy类的静态方法即可创建一个代理对象实例,其中需要传入三个参数:目标类的类加载器、目标对象实现的接口和代理处理器

我们都知道,JDK 代理是通过运行时以实现的方式创建目标类的代理类,对代理对象实现调用时执行InvocationHandler中定义的逻辑。那么首先就需要创建出一个代理对象。

创建代理对象

Java 中常见的创建对象的方法:new关键字调用构造函数创建、反序列化、clone以及通过反射动态创建对象 显然,代理类并不是编译时就确定的类,因此考虑使用反射的方式创建类对象 对象的本质是中引用方法区中 Class 元数据的对象实例,那么反射就是要在运行时访问 Class 元数据并获取构造器以进行初始化,那么目标就进一步变为运行时创建代理类的.class字节码文件

Proxy.newProxyInstance()方法中通过获取代理类的构造器并对其进行实例化

// java.lang.reflect.Proxy
public static Object newProxyInstance(ClassLoader loader,
									  Class<?>[] interfaces,
									  InvocationHandler h) {
	Objects.requireNonNull(h);

	@SuppressWarnings("removal")
	final Class<?> caller = System.getSecurityManager() == null
								? null
								: Reflection.getCallerClass();

	/*
	 * Look up or generate the designated proxy class and its constructor.
	 */
	Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);

	return newProxyInstance(caller, cons, h);
}

观察获取构造器方法中的重点部分,维护了一个proxyCache,若目标代理类不在缓存中则创建新的代理类

jdk 代理中判断是否要获取同一个构造器的逻辑为检查它们所要实现的接口是否相同以及类加载器是否一致

// java.lang.reflect.Proxy
private static Constructor<?> getProxyConstructor(Class<?> caller,
												  ClassLoader loader,
												  Class<?>... interfaces)
{
	// ...忽略优化与检查操作
	return proxyCache.sub(intfs).computeIfAbsent(
		loader,
		(ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
	);
}

先上源码

// java.lang.reflect.Proxy
Constructor<?> build() {
	Class<?> proxyClass = defineProxyClass(module, interfaces);
}

private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
	String proxyPkg = null;     // package to define proxy class in
	int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
	boolean nonExported = false;

	// 检查非 public 接口包名是否一致
	for (Class<?> intf : interfaces) {
		int flags = intf.getModifiers();
		if (!Modifier.isPublic(flags)) {
			accessFlags = Modifier.FINAL;  // non-public, 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())) {
				// module-private types
				nonExported = true;
			}
		}
	}

	if (proxyPkg == null) {
		// all proxy interfaces are public and exported
		if (!m.isNamed())
			throw new InternalError("ununamed module: " + m);
		proxyPkg = nonExported ? PROXY_PACKAGE_PREFIX + "." + m.getName()
							   : m.getName();
	} else if (proxyPkg.isEmpty() && m.isNamed()) {
		throw new IllegalArgumentException(
				"Unnamed package cannot be added to " + m);
	}

	if (m.isNamed()) {
		if (!m.getDescriptor().packages().contains(proxyPkg)) {
			throw new InternalError(proxyPkg + " not exist in " + m.getName());
		}
	}

	long num = nextUniqueNumber.getAndIncrement();
	String proxyName = proxyPkg.isEmpty()
							? proxyClassNamePrefix + num
							: proxyPkg + "." + proxyClassNamePrefix + num;

	ClassLoader loader = getLoader(m);
	trace(proxyName, m, loader, interfaces);

	byte[] proxyClassFile = 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;
	} catch (ClassFormatError e) {
		throw new IllegalArgumentException(e.toString());
	}
}

defineProxyClass()主要做了两件事:

  1. 检查非 public 的接口是否都来自同一个包下,为定义 public 为包私有,无法跨包访问
  2. 使用类加载器、代理类名、接口列表和访问标记,通过ProxyGenerator生成代理类的字节码

以下是ProxyGenerator生成代理对象类文件的方法,需要特别注意其中的visiti()方法的调用,其中硬编码了代理类的父类为java/lang/reflect/Proxy,也就是说代理类是一个继承了 Proxy 且实现了所有目标类接口的类

visit(
  int version,      // V14 - Java类文件版本
  int access,       // accessFlags - 类的访问标志
  String name,      // dotToSlash(className) - 类名
  String signature, // null - 泛型签名
  String superName, // JLR_PROXY - 父类名  ← 这里定义父类
  String[] interfaces // typeNames(interfaces) - 接口列表
);

以下是源码:

// java.lang.reflect.ProxyGenerator
private static final String JLR_PROXY = "java/lang/reflect/Proxy"; // 注意这里

private byte[] generateClassFile() {
	visit(V14, accessFlags, dotToSlash(className), null,
			JLR_PROXY, typeNames(interfaces));
		
	// 加入 Object 中的方法
	addProxyMethod(hashCodeMethod);
	addProxyMethod(equalsMethod);
	addProxyMethod(toStringMethod);
	
	// 加入所实现接口的所有方法
	for (Class<?> intf : interfaces) {
		for (Method m : intf.getMethods()) {
			if (!Modifier.isStatic(m.getModifiers())) {
				addProxyMethod(m, intf);
			}
		}
	}
	
	/*
	 * For each set of proxy methods with the same signature,
	 * verify that the methods' return types are compatible.
	 */
	for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
		checkReturnTypes(sigmethods);
	}
	
	generateConstructor();
	
	for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
		for (ProxyMethod pm : sigmethods) {
			// add static field for the Method object
			visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, pm.methodFieldName,
					LJLR_METHOD, null, null);
	
			// Generate code for proxy method
			pm.generateMethod(this, className);
		}
	}
	
	generateStaticInitializer();
	generateLookupAccessor();
	return toByteArray();
}

代理类的方法调用是由InvocationHandler定义的,这就意味着需要「拦截」所有的方法调用,那么如何「拦截」呢,在generateClassFile()中为代理类添加了所有实现接口的方法,那么只需要将这些方法的具体执行转发至LogInvocationHandler不就好了吗

for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
	for (ProxyMethod pm : sigmethods) {
		// add static field for the Method object
		visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, pm.methodFieldName,
				LJLR_METHOD, null, null);

		// Generate code for proxy method
		pm.generateMethod(this, className);
	}
}

pm.generateMethod(this, className);中为每个方法都生成了相同的方法体,将所有的调用转发至定义的invoke()方法

private void generateMethod(Method method, Class<?> intf) {
	// ...其他代码部分
	
	// 方法签名
	String methodName = method.getName();
	Class<?>[] parameterTypes = method.getParameterTypes();
	
	// 生成方法体
	visitMethodInsn(INVOKEINTERFACE,
				  "java/lang/reflect/InvocationHandler",
				  "invoke",
				  "(Ljava/lang/Object;" +       // proxy
				   "Ljava/lang/reflect/Method;" + // method
				   "[Ljava/lang/Object;)" +      // args
				   "Ljava/lang/Object;");
				   
   // ...其他代码部分
}

至此便得到一个拥有目标类实现接口的所有方法,并且每一个方法的调用都由InvocationHandler.invoke()处理的类的构造器