1. Java动态代理概述
Java动态代理(Dynamic Proxy)是一种在运行时动态创建代理对象,并将调用逻辑委托给指定处理器(InvocationHandler)处理的机制。其核心在于通过反射机制生成代理类,从而在不修改原始类的基础上,实现对方法调用过程的增强。
1.1 为什么需要动态代理?
-
解耦横切关注点:如日志记录、安全认证、事务控制等逻辑,与业务逻辑解耦。
-
统一增强行为:便于复用公共功能逻辑。
-
更强的灵活性:运行时决定代理逻辑,提高系统扩展性与可维护性。
1.2 动态代理的两种实现方式
-
JDK动态代理:基于接口,使用
java.lang.reflect.Proxy
生成代理类。 -
CGLIB代理:基于继承,通过字节码增强类实现代理,无需接口。
2. 静态代理与动态代理对比
2.1 静态代理
静态代理通过在编译期人为创建代理类实现业务增强。
public interface Service {
void execute();
}
public class RealService implements Service {
public void execute() {
System.out.println("执行业务逻辑");
}
}
public class StaticProxy implements Service {
private Service target;
public StaticProxy(Service target) {
this.target = target;
}
public void execute() {
System.out.println("日志记录开始");
target.execute();
System.out.println("日志记录结束");
}
}
2.2 动态代理
无需手动写代理类,在运行时自动生成。
2.3 对比总结
对比项 | 静态代理 | 动态代理 |
---|---|---|
实现方式 | 手写代理类 | 运行时生成代理类 |
是否依赖接口 | 是 | JDK需接口,CGLIB不需要 |
灵活性 | 低 | 高 |
可维护性 | 差 | 好 |
扩展性 | 差 | 强 |
2.4 适用场景
-
静态代理:结构简单、功能固定,适合逻辑明确的小系统。
-
动态代理:适合功能增强、模块复用频繁的大中型系统。
3. JDK动态代理实现详解
3.1 原理解析
JDK动态代理的核心在于java.lang.reflect.Proxy
类和InvocationHandler
接口。
3.1.1 类结构概览
-
Proxy
: JDK提供的用于动态创建代理类的工具类。 -
InvocationHandler
: 接口,定义了代理类方法被调用时的处理逻辑。
当通过Proxy.newProxyInstance()
方法创建代理对象时,JVM 会根据提供的接口列表生成一个实现这些接口的代理类,并通过字节码技术将代理方法的调用转发给指定的InvocationHandler
实例。
3.1.2 核心流程
-
用户调用
Proxy.newProxyInstance()
。 -
JVM 根据传入的接口数组生成一个新的字节码类(代理类)。
-
所有接口方法都会被转发到用户自定义的
InvocationHandler#invoke()
方法。 -
invoke()
方法中可以实现方法增强、参数修改、权限校验、日志记录等操作。
3.1.3 实现机制底层剖析
JDK动态代理在JVM层内部通过以下几个步骤实现:
-
生成代理类的字节码:JVM 利用
ProxyGenerator
类构造代理类的字节码(在 JDK 8 中可以通过 JVM 参数-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
保存文件)。 -
加载字节码到内存:通过定义类加载器将生成的字节码加载为一个真正的 Java 类。
-
创建代理对象实例:使用反射调用构造函数,传入
InvocationHandler
实例。
3.1.4 特点
-
动态性强,运行时生成代码。
-
要求目标类必须实现接口。
-
无法代理类本身(如 final 类或无接口类)。
-
性能略低于静态代理和CGLIB代理(因反射开销)。
3.2 实现步骤详解
使用JDK动态代理通常需要以下几个步骤:
步骤1:定义接口
代理的目标类必须实现接口。
public interface UserService {
void addUser(String username);
}
步骤2:实现接口的目标类
public class UserServiceImpl implements UserService {
public void addUser(String username) {
System.out.println("添加用户:" + username);
}
}
步骤3:创建 InvocationHandler 实现类
该类定义代理对象调用方法时的增强逻辑。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LogInvocationHandler implements InvocationHandler {
private Object target;
public LogInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法调用前: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("方法调用后: " + method.getName());
return result;
}
}
步骤4:创建代理对象
通过 Proxy.newProxyInstance
生成代理对象。
import java.lang.reflect.Proxy;
public class ProxyDemo {
public static void main(String[] args) {
UserService target = new UserServiceImpl();
LogInvocationHandler handler = new LogInvocationHandler(target);
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
proxy.addUser("张三");
}
}
3.3 示例代码运行输出
方法调用前: addUser
添加用户:张三
方法调用后: addUser
通过上述步骤,可以实现一个功能清晰、结构可扩展的JDK动态代理。
3.4 查看代理类结构:反编译输出
为了深入理解JDK动态代理生成的代理类结构,我们可以通过如下方式将代理类的字节码保存并反编译查看其结构。
启用代理类保存
在运行程序时添加如下 JVM 参数:
-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true
这将使 JVM 在调用 Proxy.newProxyInstance
时将生成的代理类 .class
文件保存到当前目录。
例如,当接口为 UserService
,生成的类名类似于:
com/sun/proxy/$Proxy0.class
使用反编译工具查看结构
我们可以使用 javap
命令查看该类的字节码结构:
javap -c com.sun.proxy.$Proxy0
以下为可能的反编译输出(仅展示关键片段,供参考):
public final class com.sun.proxy.$Proxy0 extends Proxy implements UserService {
private static Method m1; // addUser
private static Method m2; // Object.toString()
private static Method m3; // Object.equals()
private static Method m4; // Object.hashCode()
public final void addUser(String name) {
handler.invoke(this, m1, new Object[]{ name });
}
public final String toString() {
handler.invoke(this, m2, null);
}
public final boolean equals(Object obj) {
handler.invoke(this, m3, new Object[]{ obj });
}
public final int hashCode() {
handler.invoke(this, m4, null);
}
}
核心观察点
-
所有方法调用都通过
handler.invoke(...)
转发。 -
即便是
Object
类的方法(如toString()
、equals()
)也会代理。 -
代理类最终继承自
java.lang.reflect.Proxy
,并实现了目标接口。
此机制说明代理类本质上是通过组合与反射实现的“行为委托”模式。
4. CGLIB动态代理实现详解
4.1 CGLIB原理解析
CGLIB(Code Generation Library)是一个强大的、高性能的字节码生成库。与JDK动态代理不同,CGLIB基于继承机制实现代理,即它通过继承目标类并覆盖方法来进行增强,因此无需目标类实现接口。
4.1.1 核心组件
-
net.sf.cglib.proxy.Enhancer
: CGLIB的核心类,用于生成代理类。 -
MethodInterceptor
: 接口,定义方法拦截逻辑,相当于 JDK 的InvocationHandler
。
4.1.2 原理概述
-
Enhancer
设置被代理的目标类。 -
为目标类生成子类(代理类),重写其方法。
-
在方法调用前后添加增强逻辑。
4.1.3 底层依赖
CGLIB 使用 ASM 字节码编辑框架直接生成 .class
文件,因此性能优于反射。
4.2 CGLIB使用步骤
以下是使用CGLIB创建动态代理的标准流程:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 目标类
public class OrderService {
public void createOrder(String orderId) {
System.out.println("创建订单:" + orderId);
}
}
// 代理逻辑
public class CglibInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("方法执行前: " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("方法执行后: " + method.getName());
return result;
}
}
// 创建代理类
public class CglibDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderService.class);
enhancer.setCallback(new CglibInterceptor());
OrderService proxy = (OrderService) enhancer.create();
proxy.createOrder("20230716");
}
}
4.3 输出结果
方法执行前: createOrder
创建订单:20230716
方法执行后: createOrder
4.4 CGLIB生成的代理类结构解析
为了让开发者更深入地理解 CGLIB 的代理机制,下面我们展示一个典型 CGLIB 生成的代理类的“转义结构”样本(即使用字节码工具反编译得到的简化 Java 结构),以说明它是如何拦截和转发方法调用的。
💡 提示:该结构是通过配置保存生成字节码并反编译得到的,部分结构已脱敏简化,着重突出代理行为。
示例:目标类 OrderService
public class OrderService {
public void createOrder(String orderId) {
System.out.println("创建订单:" + orderId);
}
public String getStatus() {
return "已创建";
}
}
反编译后的代理类结构(CGLIB生成)
public class OrderService$$EnhancerByCGLIB$$a1b2c3 extends OrderService implements Factory {
private MethodInterceptor CGLIB$CALLBACK_0;
// 构造器
public OrderService$$EnhancerByCGLIB$$a1b2c3() {
super(); // 调用父类构造器
}
// 代理方法(重写目标类方法)
public void createOrder(String orderId) {
Method m = Class.forName("OrderService").getMethod("createOrder", String.class);
CGLIB$CALLBACK_0.intercept(this, m, new Object[]{orderId}, methodProxy$0);
}
public String getStatus() {
Method m = Class.forName("OrderService").getMethod("getStatus");
return (String) CGLIB$CALLBACK_0.intercept(this, m, new Object[]{}, methodProxy$1);
}
// 方法代理缓存(性能优化)
private static MethodProxy methodProxy$0;
private static MethodProxy methodProxy$1;
static {
// MethodProxy 初始化(绑定原始类方法)
methodProxy$0 = MethodProxy.create(OrderService.class, OrderService$$EnhancerByCGLIB$$a1b2c3.class,
"void createOrder(java.lang.String)", "createOrder", "createOrder");
methodProxy$1 = MethodProxy.create(OrderService.class, OrderService$$EnhancerByCGLIB$$a1b2c3.class,
"java.lang.String getStatus()", "getStatus", "getStatus");
}
// 设置回调(代理逻辑注入)
public void setCallback(MethodInterceptor callback) {
this.CGLIB$CALLBACK_0 = callback;
}
}
核心结构说明
成员 | 描述 |
---|---|
CGLIB$CALLBACK_0 | 拦截器对象,由用户实现的 MethodInterceptor 注入 |
intercept(...) | 方法代理逻辑统一入口,调用时执行增强逻辑 |
MethodProxy 静态字段 | 方法代理缓存,避免频繁反射创建,提升性能 |
setCallback(...) | 可在运行时动态更换代理逻辑 |
extends OrderService | 说明 CGLIB 使用的是继承方式实现代理 |
implements Factory | 来自 CGLIB 内部机制,用于对象创建与回调注入 |
小结:与JDK动态代理的对比
特性 | JDK 动态代理 | CGLIB 动态代理 |
---|---|---|
基于 | 接口反射机制 | 类继承与字节码操作 |
要求 | 目标类必须实现接口 | 无需接口,但类不能为 final |
原理 | 生成实现接口的匿名类 | 生成子类并覆盖方法 |
性能 | 反射调用,略慢 | ASM 生成类,性能更优 |
适用 | AOP、RPC 等接口驱动业务 | 封装类库、通用增强类(如Spring Bean) |
小结:与JDK动态代理的对比
特性 | JDK 动态代理 | CGLIB 动态代理 |
---|---|---|
基于 | 接口反射机制 | 类继承与字节码操作 |
要求 | 目标类必须实现接口 | 无需接口,但类不能为 final |
原理 | 生成实现接口的匿名类 | 生成子类并覆盖方法 |
性能 | 反射调用,略慢 | ASM 生成类,性能更优 |
适用 | AOP、RPC 等接口驱动业务 | 封装类库、通用增强类(如Spring Bean) |