一、JDK动态代理的核心概念
JDK动态代理是Java标准库(java.lang.reflect包)提供的运行时代理生成机制,用于在不修改原始类代码的情况下,为实现了接口的类生成代理对象,从而扩展或增强其功能。其核心原理是通过反射和字节码生成,在运行时动态创建实现目标接口的代理类,并将所有方法调用转发至InvocationHandler接口的实现类,由该类完成横切逻辑(如日志、事务、权限控制)的注入。
二、JDK动态代理的核心组件
JDK动态代理的实现依赖以下三个关键组件:
1. Proxy类
Proxy是JDK动态代理的入口类,提供了newProxyInstance静态方法,用于生成代理对象。该方法的核心逻辑是:
-
校验
InvocationHandler非空; -
克隆接口数组(避免外部篡改);
-
执行安全管理器检查(若开启);
-
从缓存中获取或生成代理类;
-
实例化代理对象(传入
InvocationHandler)。
源码片段(简化):
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
Objects.requireNonNull(h);
Class<?>[] clonedInterfaces = interfaces.clone();
checkProxyAccess(loader, clonedInterfaces); // 安全检查
Class<?> proxyClass = getProxyClass0(loader, clonedInterfaces); // 获取/生成代理类
Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
return constructor.newInstance(h); // 实例化代理对象
}
2. InvocationHandler接口
InvocationHandler是代理逻辑的核心回调接口,需用户实现其invoke方法。当代理对象的方法被调用时,Proxy类会将调用转发至InvocationHandler.invoke,该方法负责:
-
执行前置增强逻辑(如日志记录);
-
通过反射调用原始对象的方法;
-
执行后置增强逻辑(如结果处理)。
源码片段:
public interface InvocationHandler {
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
-
参数说明:
-
proxy:代理对象本身(避免递归调用); -
method:被调用的目标方法(Method对象); -
args:方法参数(Object[])。
-
3. 代理类($ProxyN)
JDK动态代理生成的代理类继承自Proxy类(单继承限制,故无法代理未实现接口的类),并实现目标接口。代理类的所有方法都会重写目标接口的方法,并将调用转发至InvocationHandler.invoke。
代理类示例(反编译后):
public final class $Proxy0 extends Proxy implements UserService {
private static Method m1; // 目标方法的Method对象(缓存)
public $Proxy0(InvocationHandler h) {
super(h); // 调用Proxy的构造器,传入InvocationHandler
}
static {
try {
m1 = Class.forName("com.example.UserService").getMethod("query", new Class[0]); // 初始化目标方法的Method对象
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
@Override
public final String query() {
try {
return (String) super.h.invoke(this, m1, null); // 转发调用至InvocationHandler
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
三、JDK动态代理的实现步骤
使用JDK动态代理需遵循以下三步:
1. 定义目标接口
目标接口是代理类实现的基础,需声明原始类的业务方法。
示例:
public interface UserService {
String query(); // 查询用户信息
void update(String name); // 更新用户信息
}
2. 实现InvocationHandler
创建InvocationHandler的实现类,注入原始对象(target),并在invoke方法中编写增强逻辑。
示例:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class UserServiceInvocationHandler implements InvocationHandler {
private final Object target; // 原始对象(被代理类)
public UserServiceInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强:日志记录
System.out.println("[Before] 调用方法:" + method.getName() + ",参数:" + Arrays.toString(args));
// 反射调用原始对象的方法
Object result = method.invoke(target, args);
// 后置增强:结果处理
System.out.println("[After] 方法返回:" + result);
return result;
}
}
3. 生成代理对象
通过Proxy.newProxyInstance方法生成代理对象,需传入以下参数:
-
类加载器:目标对象的类加载器(
target.getClass().getClassLoader()); -
接口数组:目标对象实现的接口(
target.getClass().getInterfaces()); -
InvocationHandler实例:步骤2中创建的实现类。
示例:
import java.lang.reflect.Proxy;
public class DynamicProxyDemo {
public static void main(String[] args) {
// 1. 创建原始对象
UserService target = new UserServiceImpl();
// 2. 创建InvocationHandler(注入原始对象)
InvocationHandler handler = new UserServiceInvocationHandler(target);
// 3. 生成代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
// 4. 调用代理对象的方法(触发增强逻辑)
String result = proxy.query();
System.out.println("最终结果:" + result);
}
}
输出结果:
[Before] 调用方法:query,参数:[]
原始方法执行...
[After] 方法返回:查询结果
最终结果:查询结果
四、JDK动态代理的源码深度解析
JDK动态代理的核心逻辑隐藏在Proxy.newProxyInstance和getProxyClass0方法中,以下是关键步骤的源码分析:
1. 代理类缓存(proxyClassCache)
Proxy类内部维护了一个二级缓存(WeakCache),用于存储已生成的代理类。缓存的键是「类加载器 + 接口数组」,值是代理类的Supplier(供应者)。其作用是避免重复生成代理类,提升性能。
缓存逻辑:
-
当调用
getProxyClass0时,首先从缓存中获取代理类; -
若缓存中不存在,则通过
ProxyClassFactory生成新的代理类,并存入缓存。
2. 代理类生成(ProxyClassFactory)
ProxyClassFactory是Proxy类的私有静态内部类,负责生成代理类的字节码。其核心步骤是:
-
校验接口:确保接口可见、是接口、无重复;
-
生成代理类名:格式为
com.sun.proxy.$ProxyN(N为自增序号); -
生成字节码:通过
ProxyGenerator.generateProxyClass方法生成代理类的字节码(包含所有接口方法的重写逻辑); -
定义代理类:通过
defineClass0(native方法)将字节码加载为JVM可识别的Class对象。
源码片段(ProxyClassFactory.apply):
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
// 1. 校验接口
for (Class<?> intf : interfaces) {
if (!intf.isInterface()) {
throw new IllegalArgumentException(intf.getName() + " 不是接口");
}
}
// 2. 生成代理类名
String proxyName = "com.sun.proxy.$Proxy" + nextUniqueNumber.getAndIncrement();
// 3. 生成字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
// 4. 定义代理类(native方法)
return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
}
3. 方法调用转发
代理类的所有方法都会重写目标接口的方法,并将调用转发至InvocationHandler.invoke。例如,$Proxy0.query方法的字节码等价于:
public final String query() {
return (String) super.h.invoke(this, m1, null); // 转发至InvocationHandler
}
其中,super.h是Proxy类中的InvocationHandler实例(由构造器注入)。
五、JDK动态代理的优缺点
优点
-
灵活性高:无需在编译时生成代理类,所有代理逻辑在运行时动态生成,适用于需要动态扩展功能的场景(如AOP)。
-
解耦性好:横切逻辑(如日志、事务)与业务逻辑分离,代码更清晰、可维护。
-
简单易用:JDK动态代理的API(
Proxy、InvocationHandler)简单,学习成本低。
缺点
-
仅支持接口代理:JDK动态代理只能代理实现了接口的类,无法代理未实现接口的类(需使用CGLIB等第三方库)。
-
性能开销:反射调用的性能低于直接调用(但Spring等框架通过缓存
Method对象、优化反射调用等方式降低了开销)。 -
代理类生成限制:代理类的类名、包名固定(
com.sun.proxy.$ProxyN),无法自定义。
六、JDK动态代理的应用场景
JDK动态代理广泛应用于需要动态增强功能的场景,常见例子包括:
-
AOP(面向切面编程):如Spring中的事务管理(
@Transactional)、日志记录(@Log)、权限控制(@PreAuthorize)。 -
远程方法调用(RMI):客户端通过代理对象调用远程服务器上的方法,代理对象负责封装网络通信逻辑。
-
缓存代理:代理对象在调用原始方法前检查缓存,若缓存存在则直接返回,否则调用原始方法并将结果存入缓存。
-
延迟加载:代理对象在需要时才加载原始对象(如Hibernate中的延迟加载)。
七、JDK动态代理与其他代理机制的对比
|
特性 |
JDK动态代理 |
CGLIB代理 |
|---|---|---|
|
代理方式 |
实现接口 |
继承目标类 |
|
依赖 |
Java标准库( |
CGLIB库(第三方) |
|
性能 |
反射调用(有开销) |
字节码生成(性能更高) |
|
限制 |
仅支持接口代理 |
无法代理 |
|
应用场景 |
需要接口代理的场景(如AOP) |
未实现接口的类(如实体类) |
八、JDK动态代理的优化策略
为了提升JDK动态代理的性能,Spring等框架采取了以下优化措施:
-
缓存
Method对象:避免重复获取Method对象(如method.invoke中的Method参数),减少反射开销。 -
代理对象缓存:缓存已生成的代理对象(如Spring的
ProxyFactory),避免重复创建。 -
优化反射调用:使用
MethodHandle(Java 7+)替代Method.invoke,提升反射性能。 -
字节码增强:使用ASM等字节码操作库优化代理类的生成(如Spring 6.x中对CGLIB的优化)。
总结
JDK动态代理是Java原生支持的动态代理机制,通过Proxy类和InvocationHandler接口实现,用于为实现了接口的类生成代理对象。其核心原理是运行时生成代理类,并将方法调用转发至InvocationHandler,从而实现横切逻辑的注入。JDK动态代理具有灵活性高、解耦性好的优点,适用于AOP、RMI等场景,但仅支持接口代理,性能略低于CGLIB等第三方库。在实际开发中,需根据场景选择合适的代理机制(如接口代理用JDK,类代理用CGLIB)。
23万+

被折叠的 条评论
为什么被折叠?



