什么是JDK动态代理
(有动态代理,就有静态代理,参见:多线程03--静态代理模式_runnable接口静态代理模式-CSDN博客)
JDK动态代理是Java提供的一种动态生成代理对象的机制,允许在运行时创建一个实现了指定接口的代理类。它主要用于在不修改目标对象的情况下,为目标对象添加额外的功能,比如日志记录、权限检查、事务管理等。
编码实现
代码比较简单,但核心的步骤及实现细节都有,主要分析在后面。
定义一个顶层接口
package com.ldzn.normal.proxy.dynamic;
/**
* @FileName: IService
* @Description: 基类接口
* @Author: liulianglin
* @Date: 2024/11/26:11:32
*/
public interface IService {
void myselfMethod();
}
被代理的对象
package com.ldzn.normal.proxy.dynamic;
/**
* @FileName: RealObjectService
* @Description: 真的对象服务
* @Author: liulianglin
* @Date: 2024/11/26:11:33
*/
public class RealObjectService implements IService{
@Override
public void myselfMethod() {
System.out.println("RealObjectService test .......");
}
}
自定义个动态代理处理类
package com.ldzn.normal.proxy.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @FileName: DynamicInvocationHandler
* @Description: 动态代理处理类
* @Author: liulianglin
* @Date: 2024/11/26:11:34
*
* 1.拿到被代理对象的引用,然后获取它的接口
* 2.jdk代理重新生成一个类,同时实现我们个额的代理对象所实现的接口
* 3.把被代理对象的引用也拿到了
* 4.重新动态生成一个class字节码
* 5.然后编译
*
*/
public class DynamicInvocationHandler implements InvocationHandler {
private Object target;
public Object getInstance(IService target) throws Exception {
this.target = target;
Class clazz = target.getClass();
System.out.println("被代理对象的class是:"+ clazz);
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始进行方法增强,(记录日志、校验、性能统计等),,,,");
Object result = method.invoke(target, args);
System.out.println("方法增强完毕,(记录日志、校验、性能统计等),,,,,");
return result;
}
}
测试调用
package com.ldzn.normal.proxy.dynamic;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @FileName: MainTest
* @Description:
* @Author: liulianglin
* @Date: 2024/11/26:12:10
*/
public class MainTest {
public static void printClassInfo(Executable[] targets) {
for (Executable target : targets) {
// 构造器/方法名称
String name = target.getName();
StringBuilder sBuilder = new StringBuilder(name);
// 拼接左括号
sBuilder.append('(');
Class<?>[] clazzParams = target.getParameterTypes();
// 拼接参数
for (Class<?> clazzParam : clazzParams) {
sBuilder.append(clazzParam.getName()).append(',');
}
//删除最后一个参数的逗号
if (clazzParams.length != 0) {
sBuilder.deleteCharAt(sBuilder.length() - 1);
}
//拼接右括号
sBuilder.append(')');
//打印 构造器/方法
System.out.println(sBuilder.toString());
}
}
/**
* 将动态代理生成的代理类的字节码保存到本地磁盘,方便调试查看
* @param path
* @throws ClassNotFoundException
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws IOException
*/
public static void saveProxyClassFile(String path) {
Class cl = null;
FileOutputStream fileOutputStream = null;
try {
// Java11开始ProxyGenerator,不再public,改为了private,无法直接使用,所以采用反射的方式获取它
cl = Class.forName("java.lang.reflect.ProxyGenerator");
Method m =cl.getDeclaredMethod("generateProxyClass",String.class,Class[].class);
m.setAccessible(true);
byte[] $proxy1 = (byte[]) m.invoke(null, "$proxy1",
RealObjectService.class.getInterfaces());
System.out.println($proxy1.length);
fileOutputStream = new FileOutputStream((path + "$Proxy.class"));
fileOutputStream.write($proxy1);
}catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (Exception e){
e.printStackTrace();
} finally {
if (fileOutputStream !=null) {
try {
fileOutputStream.flush();
fileOutputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
public static void main(String[] args) {
try {
IService iService = (IService) new DynamicInvocationHandler().getInstance(new RealObjectService());
iService.myselfMethod();
// 将生成的动态代理类的字节码文件保存到本地
// saveProxyClassFile("G:\\JAVA_workspace\\concurrent_programming\\");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
动态代理类字节码文件分析
动态代理类字节码的生成与加载过程
动态代理的本质就是在运行时动态的生成一个代理类,这个代理类的加载同样遵循JVM类加载机制那一套东西,涉及到动态生成代理类的字节码并将其加载到JVM中。下面是它的大概流程(下面的内容看着可能暂时会比较懵逼!!! 先混个眼熟,对过程有个大概了解,后面源码分析章节会详细分析,再整体串起来):
1. 确定类加载器
首先,Proxy.newProxyInstance
方法接收一个 ClassLoader
参数,这个参数指定了用于加载代理类的类加载器。通常,这个类加载器是目标对象的类加载器,但也可以是其他任意的类加载器。
2. 生成代理类的字节码(核心)
JDK动态代理通过以下关键步骤生成代理类的字节码:
-
创建代理类名称:代理类的名称是由
"$Proxy" + 一个数字序号
构成的,例如$Proxy0
、$Proxy1
等。这个序号是为了保证代理类的名称唯一性。 -
实现接口:代理类实现了传入
newProxyInstance
方法的所有接口。 -
添加构造方法:代理类有一个私有的构造方法,它接受一个
InvocationHandler
类型的参数。 -
生成方法实现:对于每个接口方法,代理类都会生成一个实现,该实现在调用目标方法之前和之后会调用
InvocationHandler
的invoke
方法。 -
字节码生成:JDK内部使用
ProxyBuilder
类(在 JDK 内部,不是公开API)来生成代理类的字节码。
3. 加载代理类(JVM的那一套)
生成的字节码被传递给类加载器,由类加载器负责将字节码加载到JVM中,创建代理类的 Class
对象。这个过程包括:
-
定义类:类加载器使用
defineClass
方法将字节码定义为一个类。 -
链接类:链接过程包括验证字节码、准备类、解析符号引用等步骤。
-
初始化类:对于非抽象的代理类,JVM会执行类的初始化,包括执行类构造器
<clinit>()
。
4. 创建代理实例(使用过程)
一旦代理类被加载和链接,就可以使用 newProxyInstance
方法返回的 Constructor
对象来创建代理实例。这个构造器的 newInstance
方法接收一个 InvocationHandler
实例作为参数,并返回代理对象。
5. 方法调用和拦截(使用过程)
当代理对象的方法被调用时,JVM会跳转到代理类中的方法实现,这些实现会委托给 InvocationHandler
的 invoke
方法来处理。invoke
方法负责执行实际的逻辑,包括调用目标对象的方法和添加额外的处理。
字节码文件生成
/**
* 将动态代理生成的代理类的字节码保存到本地磁盘,方便调试查看
* @param path
* @throws ClassNotFoundException
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws IOException
*/
public static void saveProxyClassFile(String path) {
Class cl = null;
FileOutputStream fileOutputStream = null;
try {
// Java11开始ProxyGenerator,不再public,改为了private,无法直接使用,所以采用反射的方式获取它
cl = Class.forName("java.lang.reflect.ProxyGenerator");
Method m =cl.getDeclaredMethod("generateProxyClass",String.class,Class[].class);
m.setAccessible(true);
byte[] $proxy1 = (byte[]) m.invoke(null, "$proxy1",
RealObjectService.class.getInterfaces());
System.out.println($proxy1.length);
fileOutputStream = new FileOutputStream((path + "$Proxy.class"));
fileOutputStream.write($proxy1);
}catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (Exception e){
e.printStackTrace();
} finally {
if (fileOutputStream !=null) {
try {
fileOutputStream.flush();
fileOutputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
字节码文件示例
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import com.ldzn.normal.proxy.dynamic.IService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $proxy1 extends Proxy implements IService {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $proxy1(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 myselfMethod() 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.ldzn.normal.proxy.dynamic.IService").getMethod("myselfMethod");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(((Throwable)var2).getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(((Throwable)var3).getMessage());
}
}
}
字节码文件分析
从上面的字节码文件中可以看到,生成的动态代理类继承了Proxy类,并实现了IService接口(我们自己的顶层接口),在它的static代码块中(即Class文件中),通过反射的方式为其内部的多个Method对象赋值,这些方法即包括Object基类包含的一些方法(如hashcode等),最关键的包含我们的IService中的myselfMethod方法(引用为m3)。同时动态代理类重写了Object基类中的方法和我们IService中的myselfMethod方法,现在看myselfMethod方法的实现,它调用super.h.invoke(this, m3, (Object[])null); 这里的super指的是Proxy父类,h表示Proxy父类的InvocationHandler变量。
这里可能有几个疑问(后边会回过头再一一回答):
1)Proxy父类的InvocationHandler变量是什么时候赋值的?
2)这个invoke执行的是谁的方法?
源码分析
newProxyInstance源码分析(入口)
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
// 确保传入的 InvocationHandler 对象不为 null,如果为 null 则抛出 NullPointerException
Objects.requireNonNull(h);
// 如果存在安全管理器,则获取调用者的类对象(即调用 newProxyInstance 方法的类的类对象)
// 否则,caller 设置为 null
final Class<?> caller = System.getSecurityManager() == null
? null
: Reflection.getCallerClass();
/*
* 查找或生成指定的代理类及其构造器。
* 这个方法会检查是否已经缓存了对应的代理类,如果没有,则会生成一个新的代理类
* 并缓存它。生成的代理类实现了传入的 interfaces 数组中声明的所有接口。
*/
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
// 使用查找或生成的构造器创建一个新的代理实例
// 这个方法会使用反射来调用代理类的构造器,传入的 InvocationHandler实例h会被赋值为Proxy的h变量
return newProxyInstance(caller, cons, h);
}
使用Proxy.newProxyInstance方法时,JVM会获取代理类的构造函数,并将InvocationHandler实例作为参数传入构造函数,后面用这个构造方法来实例化代理对象 。
getProxyConstructor
源码分析
private static Constructor<?> getProxyConstructor(Class<?> caller,
ClassLoader loader,
Class<?>... interfaces)
{
// optimization for single interface
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 {
// interfaces cloned
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()
);
}
}
分析:
-
单接口优化:
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() ); }
如果代理类只需要实现一个接口,代码会进行优化处理。首先,获取这个单一接口
intf
。如果调用者caller
不为null
,则调用checkProxyAccess
方法检查是否有权限创建代理类。然后,使用proxyCache
缓存来获取或生成代理类的构造器。proxyCache
是一个多层次的缓存结构,其中sub(intf)
获取或创建与接口相关的子缓存,computeIfAbsent
方法确保对于给定的类加载器loader
,只生成一次代理类。 -
多接口处理:
} else { // interfaces cloned 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() ); }
如果代理类需要实现多个接口,代码会克隆接口数组以避免修改原始数组。如果调用者
caller
不为null
,则调用checkProxyAccess
方法检查权限。然后,将接口数组转换为列表intfs
。与单接口的情况类似,使用proxyCache
缓存来获取或生成代理类的构造器。这里,proxyCache
的子缓存是基于接口列表创建的,确保对于给定的类加载器和接口列表,只生成一次代理类。
3.代理类构建
核心!!!!,参加下一个章节内容
new ProxyBuilder(ld, clv.key()).build()
ProxyBuilder源码分析
构造方法:
ProxyBuilder(ClassLoader loader, List<Class<?>> interfaces) {
// 检查 JVM 的模块系统是否已经完全初始化。如果没有,会抛出 InternalError 异常,因为动态代理在模块系统未完全初始化之前是不支持的
if (!VM.isModuleSystemInited()) {
throw new InternalError("Proxy is not supported until "
+ "module system is fully initialized");
}
// 检查传入的接口列表 interfaces 的大小是否超过了 65535,这是由于内部实现的限制。如果超过,会抛出 IllegalArgumentException 异常
if (interfaces.size() > 65535) {
throw new IllegalArgumentException("interface limit exceeded: "
+ interfaces.size());
}
// 调用 referencedTypes 方法来获取所有被引用的类型,并存储在 Set<Class<?>> 中
Set<Class<?>> refTypes = referencedTypes(loader, interfaces);
// 验证代理接口是否符合 newProxyInstance 方法中指定的限制条件
validateProxyInterfaces(loader, interfaces, refTypes);
// 成员变量初始化
this.interfaces = interfaces;
this.module = mapToModule(loader, interfaces, refTypes);
assert getLoader(module) == loader;
}
build方法
Constructor<?> build() {
// 调用 defineProxyClass 方法来定义代理类。这个方法负责生成代理类的字节码,并将其加载到 JVM 中
Class<?> proxyClass = defineProxyClass(module, interfaces);
final Constructor<?> cons;
try {
/*
通过代理类的 Class 对象,尝试获取其构造器 Constructor。构造器的参数类型是 InvocationHandler 类型,这是因为每个代理对象都需要一个 InvocationHandler 来处理方法调用
private static final Class<?>[] constructorParams ={InvocationHandler.class };
*/
cons = proxyClass.getConstructor(constructorParams);
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
/*
使用 AccessController.doPrivileged 来设置构造器的可访问性为 true,这样就可以在代码中创建代理实例,即使构造器是私有的
*/
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
// 返回构造器
return cons;
}
defineProxyClass源码分析
private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
// 代理类的包名及访问标识
String proxyPkg = null;
int accessFlags = Modifier.PUBLIC | Modifier.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.
*/
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");
}
}
}
if (proxyPkg == null) {
// all proxy interfaces are public
proxyPkg = m.isNamed() ? PROXY_PACKAGE_PREFIX + "." + m.getName()
: PROXY_PACKAGE_PREFIX;
} 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());
}
}
/*
* 生成代理类的名称,一般是$proxy0、$proxy1等
proxyName : com.sun.proxy.$Proxy0
*/
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(
proxyName, interfaces.toArray(EMPTY_CLASS_ARRAY), accessFlags);
try {
// 定义代理类并返回,JLA被用来通过反射来访问ClassLoader的defineClass方法,这是一个native方法,用于将传入的字节码转proxyClassFile换成Java类,加载到JVM中
Class<?> pc = JLA.defineClass(loader, proxyName, proxyClassFile,
null, "__dynamic_proxy__");
reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
return pc;
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
返回的代理类
答疑
还记得前面的问题吗,现在一一来揭开面纱
1)Proxy父类的InvocationHandler变量是什么时候赋值的?
代理类的构造器需要一个InvocationHandler
参数,这个参数在创建代理实例时被传递给Proxy
类的构造器,从而初始化Proxy
类中的h
字段。具体是在newProxyInstance(caller, cons, h);方法实现的。这个方法会使用反射来调用代理类的构造器,传入的 InvocationHandler实例h会被赋值为Proxy的h变量。
2)这个invoke执行的是谁的方法
这个invoke执行的就是DynamicInvocationHandler 中的invoke方法。
总结
通过上面的内容描述,现在基本的脉络大致有个浅显的了解,现在再整体串一下JDK动态代理的工作原理。
1)编码层工作
定义好顶层接口、需要代理的类、动态代理类处理器。
2)代理对象时怎么生成的(核心)
通过调用Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成的,参数说明:
- ClassLoader loader :类加载器,用于将最后生成的动态代理类Class文件加载进JVM中。
- Class<?>[] interfaces: 需要代理的类实现的所有接口。
- InvocationHandler h:指的就是我们实现的动态代理类处理器,里边的invoke方法用于实现对被代理对象中方法的调用和服务增强。
在生成代理对象的过程中,大致会经历如下几个关键步骤(没有列的也不代表不重要):
- 通过ProxyGenerator.generateProxyClass生成动态代理类的字节码。
- 然后使用JLA.defineClass将字节码数组生成动态代理类的Class对象,这个创建的
Class
对象被存储在 JVM 的堆内存中。 - 然后调用proxyClass.getConstructor获取动态代理类的类加载器。
- 使用类加载器的newInstance方法生成一个动态代理类实例对象
3)代理对象是怎么工作
当我们调用代理对象的方法时,其实代理对象中的这个方法会调用Proxy的InvocationHandler实例的invoke方法,而这个InvocationHandler就是我们在上面newProxyInstance传入的我们自己实现的动态类处理器。
public final void myselfMethod() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }