一、Java 中的动态代理类型
Java 中常见的动态代理有两种主要类型:
- JDK 动态代理
- CGLIB 动态代理
二、JDK 动态代理
✨ 实现方式:
使用 Java 标准库中的 java.lang.reflect.Proxy
和 InvocationHandler
。
✅ 优点:
-
不依赖第三方库。
-
简单轻量,标准库支持。
-
非常适合接口驱动设计的系统(如 DAO 接口)。
❌ 缺点:
- 只能代理接口(不能代理普通类)。
✅ 使用场景:
- 接口型编程,如 Spring 中的基于接口的 AOP。
- MyBatis Mapper 接口。
流程图
sequenceDiagram
participant Client as 客户端代码
participant Proxy as 动态代理对象 (proxy)
participant Handler as InvocationHandler 实例
participant Target as 真实目标对象
Client->>Proxy: 调用 proxy.method()
Proxy->>Handler: 调用 invoke(proxy, method, args)
Handler->>Handler: 前置增强(日志、权限等)
Handler->>Target: 反射调用目标方法 method.invoke(target, args)
Target-->>Handler: 返回结果
Handler->>Handler: 后置增强(日志、事务等)
Handler-->>Proxy: 返回结果
Proxy-->>Client: 返回最终结果### **Mermaid**
示例代码:
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 定义一个接口,表示服务的功能
interface UserService {
// 接口方法:打招呼
void sayHello(String name);
}
// 实现接口的类,提供具体的功能实现
class UserServiceImpl implements UserService {
// 实现接口中的方法
public void sayHello(String name) {
System.out.println("Hello, " + name); // 打印问候语
}
}
// InvocationHandler 的实现类,用于增强目标对象的方法
class LoggingHandler implements InvocationHandler {
private final Object target; // 被代理的目标对象
// 构造函数,接收目标对象
public LoggingHandler(Object target) {
this.target = target;
}
// invoke 方法会在代理对象调用方法时被触发
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在方法调用前打印日志信息
System.out.println("Before method: " + method.getName()); // 打印方法名
// 调用目标对象的实际方法,并传递参数
Object result = method.invoke(target, args);
// 在方法调用后打印日志信息
System.out.println("After method: " + method.getName()); // 打印方法名
return result; // 返回方法执行的结果
}
}
// 主类,用于测试动态代理功能
public class Main {
public static void main(String[] args) {
// 创建目标对象(实际的服务实现)
UserService realService = new UserServiceImpl();
// 使用 Proxy.newProxyInstance 创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
realService.getClass().getClassLoader(), // 目标对象的类加载器
new Class[]{UserService.class}, // 目标对象实现的接口数组
new LoggingHandler(realService) // 自定义的 InvocationHandler 实现
);
// 调用代理对象的方法,实际会触发 LoggingHandler 中的 invoke 方法
proxy.sayHello("Alice");
}
}
输出结果
sql
Before method: sayHello
Hello, Alice
After method: sayHello
三、CGLIB 动态代理
✨ 实现方式:
通过继承目标类,重写方法来实现代理。底层使用字节码生成技术(如 ASM)动态创建子类。
常用库:cglib(如在 Spring AOP 中用于非接口代理)。
✅ 优点:
-
可以代理普通类(非接口) 。
-
性能比 JDK 动态代理略高(尤其是大量方法调用场景)。
❌ 缺点:
-
被代理类 不能是 final 类。
-
被代理方法 不能是 final/static/private。
-
依赖第三方库(如 CGLIB 或 ByteBuddy)。
✅ 使用场景:
- 没有接口的类。
- 需要代理已有类(如 Controller、Service 等 Bean)。
流畅图
sequenceDiagram
participant Client as 客户端代码
participant Proxy as CGLIB 生成的子类对象
participant Interceptor as MethodInterceptor 实例
participant SuperClass as 目标类(被代理对象)
Client->>Proxy: 调用 proxy.method()
Proxy->>Interceptor: 调用 intercept(proxy, method, args, methodProxy)
Interceptor->>Interceptor: 前置增强逻辑(日志、事务等)
Interceptor->>SuperClass: methodProxy.invokeSuper(proxy, args)
SuperClass-->>Interceptor: 返回原始方法执行结果
Interceptor->>Interceptor: 后置增强逻辑
Interceptor-->>Proxy: 返回最终结果
Proxy-->>Client: 返回最终结果
示例代码:
java
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 目标类,不需要实现接口
class UserService {
// 目标方法:打招呼
public void sayHello(String name) {
System.out.println("Hello, " + name); // 打印问候语
}
}
// MethodInterceptor 的实现类,用于增强目标类的方法
class LoggingInterceptor implements MethodInterceptor {
/**
* intercept 方法会在代理对象调用方法时被触发
* @param obj 代理对象本身
* @param method 被拦截的方法对象
* @param args 方法参数
* @param proxy 方法代理对象,用于调用父类方法
* @return 方法执行的结果
* @throws Throwable 抛出异常
*/
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 在方法调用前打印日志信息
System.out.println("Before method: " + method.getName()); // 打印方法名
// 使用 MethodProxy 调用父类(目标类)的实际方法
Object result = proxy.invokeSuper(obj, args);
// 在方法调用后打印日志信息
System.out.println("After method: " + method.getName()); // 打印方法名
return result; // 返回方法执行的结果
}
}
// 主类,用于测试 CGLIB 动态代理功能
public class Main {
public static void main(String[] args) {
// 创建 Enhancer 对象,用于生成代理类
Enhancer enhancer = new Enhancer();
// 设置目标类作为父类,CGLIB 会动态生成该类的子类,注意这里是调用 invokeSuper 而不是 invoke,否则死循环,invokeSuper方法会直接调用目标类(即被代理的类)的方法实现,而不会再次触发代理逻辑。这是因为在 CGLIB 中,代理类是目标类的子类,`invokeSuper` 实际上调用了父类的方法
enhancer.setSuperclass(UserService.class);
// 设置回调函数,使用自定义的 MethodInterceptor 实现
enhancer.setCallback(new LoggingInterceptor());
// 创建代理对象,实际是目标类的子类实例
UserService proxy = (UserService) enhancer.create();
// 调用代理对象的方法,实际会触发 LoggingInterceptor 中的 intercept 方法
proxy.sayHello("Bob");
}
}
输出结果
sql
Before method: sayHello
Hello, Alice
After method: sayHello
四、JDK 与 CGLIB 的对比总结
对比项 | JDK 动态代理 | CGLIB 动态代理 |
---|---|---|
代理对象 | 接口 | 类 |
是否要求接口 | 是 | 否 |
底层实现 | 反射 + Proxy | 字节码生成(ASM) |
性能 | 较低 | 较高 |
限制 | 只能代理接口 | 类不能是 final,方法不能是 final/private ,通过继承 |
框架支持 | Java 标准库 | 需引入第三方库 |
使用示例 | MyBatis Mapper、Spring AOP | Spring AOP、Hibernate、RPC 框架等 |
五、其他动态代理方式(了解)
-
ByteBuddy
- 更现代、灵活的字节码操作库(替代 CGLIB)。
- 用于 JDK 代理或更复杂的类构建。
- Spring Boot 2.x+ 使用 ByteBuddy 生成代理类。
-
Javassist
- 操作 Java 字节码,生成新类或修改已有类。
-
ASM
- 底层字节码编辑器,CGLIB 的基础。
- 性能高,使用复杂,一般用于框架开发。
六、动态代理的常见使用场景总结
- AOP 切面编程:日志、事务、权限校验。
- 远程调用代理(RPC) :如 Dubbo。
- 懒加载与缓存:Hibernate。
- 拦截器链实现。
- Mock 框架:如 Mockito 生成 mock 对象。