动态代理
问:什么是动态代理?JDK 和 CGLIB 有什么区别?
动态代理就是一种“运行时帮你生成代理对象”的机制,它的最大特点是:你不需要提前写好代理类,而是在程序运行的时候,框架帮你“临时织入”增强逻辑。
在不动原来业务代码的前提下,它可以帮你在方法执行的前后插入各种通用逻辑,比如日志打印、权限校验、事务控制等,这在 AOP、RPC、监控等场景中非常常见。
Java 有两种动态代理方式:
- JDK 动态代理:专门用来代理“接口”,它通过
Proxy和InvocationHandler实现,是基于反射的;- CGLIB 动态代理:用来代理“类”,不需要接口,是通过继承 + 动态生成字节码的方式实现的。
简单理解就是: 👉 有接口就用 JDK,没接口就用 CGLIB。
在 Spring 中:
- 传统 Spring 默认用的是 JDK 动态代理;
- 从 Spring Boot 2.x 开始,默认改用 CGLIB,因为它兼容性更强,避免因为类没有接口而出错。
第一部分:问题的提出
一、现实中的需求:横切关注点
想象一个场景:
// 传统方式:业务代码和增强逻辑混在一起
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
// 日志打印
System.out.println("保存用户前:" + user);
// 权限检查
if (!hasPermission()) {
throw new SecurityException("无权限");
}
// 事务开始
beginTransaction();
try {
// 真正的业务逻辑
userRepository.save(user);
// 事务提交
commitTransaction();
} catch (Exception e) {
// 事务回滚
rollbackTransaction();
throw e;
}
// 日志打印
System.out.println("保存用户后");
}
@Override
public void delete(int id) {
// 又要重复写一遍日志、权限、事务的代码...
System.out.println("删除用户前:" + id);
if (!hasPermission()) {
throw new SecurityException("无权限");
}
beginTransaction();
try {
userRepository.delete(id);
commitTransaction();
} catch (Exception e) {
rollbackTransaction();
throw e;
}
System.out.println("删除用户后");
}
}
这种写法有什么问题?
- 代码重复 - 日志、权限、事务代码在每个方法中重复
- 职责混乱 - 业务逻辑和增强逻辑混在一起
- 难以维护 - 修改增强逻辑需要改动所有方法
- 难以测试 - 无法单独测试业务逻辑
二、理想的解决方案
我们希望能这样写:
// 理想方式:业务代码只关注业务逻辑
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
// 只写业务逻辑,增强逻辑由框架自动处理
userRepository.save(user);
}
@Override
public void delete(int id) {
// 只写业务逻辑
userRepository.delete(id);
}
}
// 框架自动为我们处理:日志、权限、事务等
UserService userService = createProxy(new UserServiceImpl());
userService.save(user); // 自动执行:日志 → 权限检查 → 事务 → 业务逻辑 → 事务提交 → 日志
这就是动态代理要解决的问题:在不改变原始代码的前提下,为方法自动织入增强逻辑。
第二部分:解决方案的演进
三、静态代理:手工方案
在动态代理出现之前,我们可以手工写代理类:
// 1. 定义接口
public interface UserService {
void save(User user);
void delete(int id);
}
// 2. 实现接口
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
userRepository.save(user);
}
@Override
public void delete(int id) {
userRepository.delete(id);
}
}
// 3. 手工写代理类
public class UserServiceProxy implements UserService {
private UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void save(User user) {
// 前置增强
System.out.println("保存用户前");
checkPermission();
beginTransaction();
try {
// 调用原方法
target.save(user);
// 后置增强
commitTransaction();
} catch (Exception e) {
rollbackTransaction();
throw e;
}
System.out.println("保存用户后");
}
@Override
public void delete(int id) {
// 又要写一遍代理逻辑...
System.out.println("删除用户前");
checkPermission();
beginTransaction();
try {
target.delete(id);
commitTransaction();
} catch (Exception e) {
rollbackTransaction();
throw e;
}
System.out.println("删除用户后");
}
}
// 4. 使用代理
UserService userService = new UserServiceProxy(new UserServiceImpl());
userService.save(user);
静态代理的问题:
- 需要手工为每个接口写代理类
- 如果有100个接口,就要写100个代理类
- 代理逻辑重复,难以维护
四、动态代理:自动方案
动态代理就是让框架自动生成代理类,而不需要我们手工写。
// 动态代理:框架自动生成代理类
UserService target = new UserServiceImpl();
UserService proxy = createDynamicProxy(target, (method, args) -> {
// 前置增强
System.out.println("方法调用前:" + method.getName());
checkPermission();
beginTransaction();
try {
// 调用原方法
Object result = method.invoke(target, args);
// 后置增强
commitTransaction();
return result;
} catch (Exception e) {
rollbackTransaction();
throw e;
}
});
// 所有方法都自动应用了增强逻辑
proxy.save(user);
proxy.delete(1);
动态代理的优势:
- 无需手工写代理类
- 一套增强逻辑适用于所有方法
- 易于维护和扩展
第三部分:动态代理的两种实现
五、JDK动态代理:基于接口
5.1 设计思路
┌─────────────────────────────────────────┐
│ JDK动态代理的设计思路 │
├─────────────────────────────────────────┤
│ │
│ 1. 前提条件 │
│ └─ 目标类必须实现接口 │
│ │
│ 2. 核心机制 │
│ ├─ Proxy:代理工厂 │
│ ├─ InvocationHandler:方法拦截器 │
│ └─ 反射:调用原方法 │
│ │
│ 3. 工作流程 │
│ ├─ 调用代理对象的方法 │
│ ├─ 转发到InvocationHandler.invoke() │
│ ├─ 通过反射调用原方法 │
│ └─ 返回结果 │
│ │
└─────────────────────────────────────────┘
5.2 核心API
// Proxy.newProxyInstance() 的三个参数
Proxy.newProxyInstance(
ClassLoader loader, // 1. 类加载器
Class<?>[] interfaces, // 2. 要实现的接口
InvocationHandler handler // 3. 方法拦截器
);
5.3 完整实现
// 1. 定义接口
public interface UserService {
void save(User user);
String getName(int id);
}
// 2. 实现接口
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
System.out.println("真正的保存逻辑:" + user);
}
@Override
public String getName(int id) {
System.out.println("真正的查询逻辑:" + id);
return "Tom";
}
}
// 3. 实现InvocationHandler(方法拦截器)
public class MyInvocationHandler implements InvocationHandler {
private Object target; // 被代理的目标对象
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// proxy:代理对象本身(通常不用)
// method:被调用的方法
// args:方法参数
// 前置增强
System.out.println("========== 方法调用前 ==========");
System.out.println("方法名:" + method.getName());
System.out.println("参数:" + Arrays.toString(args));
// 调用原方法(通过反射)
Object result = null;
try {
result = method.invoke(target, args);
System.out.println("返回值:" + result);
} catch (Exception e) {
System.out.println("异常:" + e.getMessage());
throw e;
}
// 后置增强
System.out.println("========== 方法调用后 ==========");
return result;
}
}
// 4. 创建代理对象
public class JDKProxyDemo {
public static void main(String[] args) {
// 创建目标对象
UserService target = new UserServiceImpl();
// 创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 要实现的接口
new MyInvocationHandler(target) // 方法拦截器
);
// 调用代理对象的方法
proxy.save(new User("Tom"));
String name = proxy.getName(1);
}
}
执行流程:
proxy.save(user)
↓
InvocationHandler.invoke(proxy, method, args)
↓
前置增强(日志、权限检查等)
↓
method.invoke(target, args) // 反射调用原方法
↓
后置增强(事务提交等)
↓
返回结果
5.4 JDK动态代理的原理
// JDK动态代理的本质:运行时生成一个代理类
// 生成的代理类大概是这样的:
public class $Proxy0 implements UserService {
private InvocationHandler h;
public $Proxy0(InvocationHandler h) {
this.h = h;
}
@Override
public void save(User user) {
try {
// 获取save方法的Method对象
Method method = UserService.class.getMethod("save", User.class);
// 调用InvocationHandler的invoke方法
h.invoke(this, method, new Object[]{user});
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public String getName(int id) {
try {
Method method = UserService.class.getMethod("getName", int.class);
return (String) h.invoke(this, method, new Object[]{id});
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
关键点:
- JDK动态代理在运行时生成一个实现了接口的代理类
- 代理类的每个方法都会调用InvocationHandler的invoke()
- 所有的增强逻辑都在invoke()中实现
六、CGLIB动态代理:基于继承
6.1 设计思路
┌─────────────────────────────────────────┐
│ CGLIB动态代理的设计思路 │
├─────────────────────────────────────────┤
│ │
│ 1. 前提条件 │
│ └─ 目标类可以没有接口 │
│ │
│ 2. 核心机制 │
│ ├─ Enhancer:代理工厂 │
│ ├─ MethodInterceptor:方法拦截器 │
│ └─ 字节码增强:生成子类 │
│ │
│ 3. 工作流程 │
│ ├─ 创建目标类的子类 │
│ ├─ 重写目标方法 │
│ ├─ 在子类方法中插入拦截逻辑 │
│ └─ 调用super.method()执行原方法 │
│ │
└─────────────────────────────────────────┘
6.2 核心API
// Enhancer的基本用法
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass); // 设置父类
enhancer.setCallback(methodInterceptor); // 设置拦截器
Object proxy = enhancer.create(); // 创建代理对象
6.3 完整实现
// 1. 目标类(注意:没有接口)
public class UserService {
public void save(User user) {
System.out.println("真正的保存逻辑:" + user);
}
public String getName(int id) {
System.out.println("真正的查询逻辑:" + id);
return "Tom";
}
}
// 2. 实现MethodInterceptor(方法拦截器)
public class MyMethodInterceptor implements MethodInterceptor {
private Object target; // 被代理的目标对象
public MyMethodInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
// obj:代理对象
// method:被调用的方法
// args:方法参数
// proxy:方法代理,用于调用原方法
// 前置增强
System.out.println("========== 方法调用前 ==========");
System.out.println("方法名:" + method.getName());
System.out.println("参数:" + Arrays.toString(args));
// 调用原方法(两种方式)
Object result = null;
try {
// 方式1:使用MethodProxy(推荐,性能更好)
result = proxy.invokeSuper(obj, args);
// 方式2:使用反射(不推荐)
// result = method.invoke(target, args);
System.out.println("返回值:" + result);
} catch (Exception e) {
System.out.println("异常:" + e.getMessage());
throw e;
}
// 后置增强
System.out.println("========== 方法调用后 ==========");
return result;
}
}
// 3. 创建代理对象
public class CGLIBProxyDemo {
public static void main(String[] args) {
// 创建目标对象
UserService target = new UserService();
// 创建Enhancer
Enhancer enhancer = new Enhancer();
// 设置父类(要代理的类)
enhancer.setSuperclass(UserService.class);
// 设置拦截器
enhancer.setCallback(new MyMethodInterceptor(target));
// 创建代理对象
UserService proxy = (UserService) enhancer.create();
// 调用代理对象的方法
proxy.save(new User("Tom"));
String name = proxy.getName(1);
}
}
执行流程:
proxy.save(user)
↓
MethodInterceptor.intercept(obj, method, args, proxy)
↓
前置增强(日志、权限检查等)
↓
proxy.invokeSuper(obj, args) // 调用父类方法
↓
后置增强(事务提交等)
↓
返回结果
6.4 CGLIB动态代理的原理
// CGLIB动态代理的本质:运行时生成一个继承目标类的子类
// 生成的代理类大概是这样的:
public class UserService$$EnhancerByCGLIB$$12345 extends UserService {
private MethodInterceptor methodInterceptor;
@Override
public void save(User user) {
// 获取方法代理
MethodProxy proxy = MethodProxy.find(
UserService.class,
new Class[]{User.class}
);
// 调用拦截器
methodInterceptor.intercept(
this,
UserService.class.getMethod("save", User.class),
new Object[]{user},
proxy
);
}
@Override
public String getName(int id) {
MethodProxy proxy = MethodProxy.find(
UserService.class,
new Class[]{int.class}
);
return (String) methodInterceptor.intercept(
this,
UserService.class.getMethod("getName", int.class),
new Object[]{id},
proxy
);
}
}
关键点:
- CGLIB动态代理在运行时生成一个继承目标类的子类
- 子类重写了目标类的所有方法
- 每个方法都会调用MethodInterceptor的intercept()
- 通过proxy.invokeSuper()调用父类(原始)方法
第四部分:JDK vs CGLIB对比
七、两者的核心区别
7.1 对比表格
| 维度 | JDK动态代理 | CGLIB动态代理 |
|---|---|---|
| 依赖接口 | ✅ 必须实现接口 | ❌ 不需要接口 |
| 实现方式 | 反射 + 接口实现 | 字节码增强 + 继承 |
| 代理对象 | 实现接口的代理类 | 继承目标类的子类 |
| 性能(调用) | JDK8后接近 | 稍快(避免反射) |
| 性能(创建) | 快 | 慢(字节码生成) |
| final限制 | 无影响 | ❌ 无法代理final方法 |
| 原生支持 | ✅ JDK内置 | ❌ 需要引入CGLIB |
| Spring默认 | Spring Framework | SpringBoot 2.x+ |
7.2 选择标准
┌─────────────────────────────────────────┐
│ 如何选择JDK还是CGLIB? │
├─────────────────────────────────────────┤
│ │
│ 有接口? │
│ ├─ 是 → 优先选择JDK │
│ │ ├─ 原生支持 │
│ │ ├─ 无需额外依赖 │
│ │ └─ 性能相当 │
│ │ │
│ └─ 否 → 必须选择CGLIB │
│ ├─ 支持具体类 │
│ ├─ 通用性强 │
│ └─ 避免报错 │
│ │
│ Spring框架选择: │
│ ├─ Spring Framework:JDK(默认) │
│ ├─ SpringBoot 2.x+:CGLIB(默认) │
│ └─ 可通过配置修改 │
│ │
└─────────────────────────────────────────┘
7.3 为什么SpringBoot改用CGLIB?
// 问题场景:使用JDK代理的坑
// 情况1:有接口,JDK代理正常
public interface UserService {
void save(User user);
}
public class UserServiceImpl implements UserService {
@Override
public void save(User user) { }
}
// ✅ 可以使用JDK代理
UserService proxy = (UserService) Proxy.newProxyInstance(...);
// 情况2:没有接口,JDK代理报错
public class UserService { // 没有实现接口
public void save(User user) { }
}
// ❌ JDK代理报错:java.lang.ClassCastException
// UserService proxy = (UserService) Proxy.newProxyInstance(...);
// 因为代理对象没有实现UserService接口
// ✅ CGLIB代理正常工作
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
UserService proxy = (UserService) enhancer.create();
SpringBoot改用CGLIB的原因:
- 通用性更强 - 支持有接口和无接口的类
- 避免ClassCastException - 不会因为缺接口而报错
- 易于维护 - 用户无需关心是否有接口
- 性能相当 - JDK8后性能差异已缩小
第五部分:实际应用
八、Spring AOP中的动态代理
// 1. 定义切面
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void beforeMethod(JoinPoint joinPoint) {
System.out.println("方法调用前:" + joinPoint.getSignature());
}
@After("execution(* com.example.service.*.*(..))")
public void afterMethod(JoinPoint joinPoint) {
System.out.println("方法调用后:" + joinPoint.getSignature());
}
}
// 2. 定义业务类(可以没有接口)
@Service
public class UserService {
public void save(User user) {
System.out.println("保存用户:" + user);
}
}
// 3. Spring自动创建代理
@Autowired
private UserService userService; // 实际上是代理对象
// 4. 调用代理对象
userService.save(user); // 自动执行:前置通知 → 业务逻辑 → 后置通知
Spring AOP的工作流程:
1. 扫描@Aspect注解的切面类
2. 解析@Before、@After等通知
3. 为匹配的Bean创建代理对象
4. 调用代理对象时,自动织入增强逻辑
九、RPC框架中的动态代理
// RPC框架使用动态代理模拟本地调用
// 1. 定义远程服务接口
public interface UserServiceRPC {
User getUserById(int id);
}
// 2. 创建RPC代理
public class RPCProxyFactory {
public static <T> T createProxy(Class<T> serviceInterface, String serverAddress) {
return (T) Proxy.newProxyInstance(
serviceInterface.getClassLoader(),
new Class[]{serviceInterface},
(proxy, method, args) -> {
// 1. 序列化参数
byte[] requestData = serialize(method.getName(), args);
// 2. 发送网络请求到远程服务器
byte[] responseData = sendNetworkRequest(serverAddress, requestData);
// 3. 反序列化返回值
return deserialize(responseData);
}
);
}
}
// 3. 使用RPC代理
UserServiceRPC userService = RPCProxyFactory.createProxy(
UserServiceRPC.class,
"192.168.1.100:8080"
);
// 看起来像本地调用,实际上是远程调用
User user = userService.getUserById(1);
第六部分:性能与最佳实践
十、性能对比
public class ProxyPerformanceDemo {
public static void main(String[] args) {
int iterations = 1_000_000;
// ========== 直接调用 ==========
UserService target = new UserServiceImpl();
long directStart = System.nanoTime();
for (int i = 0; i < iterations; i++) {
target.save(new User());
}
long directTime = System.nanoTime() - directStart;
// ========== JDK代理 ==========
UserService jdkProxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
new MyInvocationHandler(target)
);
long jdkStart = System.nanoTime();
for (int i = 0; i < iterations; i++) {
jdkProxy.save(new User());
}
long jdkTime = System.nanoTime() - jdkStart;
// ========== CGLIB代理 ==========
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback(new MyMethodInterceptor(target));
UserService cglibProxy = (UserService) enhancer.create();
long cglibStart = System.nanoTime();
for (int i = 0; i < iterations; i++) {
cglibProxy.save(new User());
}
long cglibTime = System.nanoTime() - cglibStart;
// 输出结果
System.out.println("直接调用:" + directTime + "ns");
System.out.println("JDK代理:" + jdkTime + "ns");
System.out.println("CGLIB代理:" + cglibTime + "ns");
}
}
典型结果(JDK8+):
直接调用:10ms
JDK代理:15ms(1.5倍)
CGLIB代理:12ms(1.2倍)
性能特点:
- JDK代理:调用快,创建快
- CGLIB代理:调用稍快,创建慢(字节码生成)
- 总体差异不大,不是选择的主要因素
十一、最佳实践
// ✅ 推荐做法
// 1. 优先使用接口
public interface UserService {
void save(User user);
}
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
// 业务逻辑
}
}
// 2. 使用Spring AOP而不是手工代理
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void beforeMethod(JoinPoint joinPoint) {
System.out.println("方法调用前");
}
}
// 3. 缓存代理对象
public class ProxyCache {
private static final Map<Class<?>, Object> cache = new ConcurrentHashMap<>();
public static <T> T getProxy(Class<T> serviceClass) {
return (T) cache.computeIfAbsent(serviceClass, key -> {
// 创建代理对象
return createProxy(serviceClass);
});
}
}
// ❌ 避免做法
// 1. 不要频繁创建代理对象
for (int i = 0; i < 1000; i++) {
UserService proxy = Proxy.newProxyInstance(...); // 每次都创建
}
// 2. 不要在代理中做重操作
public Object invoke(Object proxy, Method method, Object[] args) {
// ❌ 不要在这里做数据库查询等重操作
userRepository.queryAll();
return method.invoke(target, args);
}
// 3. 不要混用JDK和CGLIB
// 同一个应用中,尽量统一使用一种代理方式
第七部分:总结
十二、动态代理的本质
动态代理是在运行时自动生成代理类的机制,它让我们能够在不修改原始代码的前提下,为方法自动织入增强逻辑。
┌──────────────────────────────────────────┐
│ 动态代理的核心价值 │
├──────────────────────────────────────────┤
│ │
│ 1. 解耦 │
│ ├─ 业务逻辑和增强逻辑分离 │
│ ├─ 易于维护和扩展 │
│ └─ 符合单一职责原则 │
│ │
│ 2. 通用性 │
│ ├─ 一套增强逻辑适用所有方法 │
│ ├─ 无需为每个类写代理 │
│ └─ 支持动态添加功能 │
│ │
│ 3. 灵活性 │
│ ├─ 运行时动态决定是否代理 │
│ ├─ 支持多个增强逻辑组合 │
│ └─ 易于扩展新功能 │
│ │
│ 4. 框架能力 │
│ ├─ 实现AOP(面向切面编程) │
│ ├─ 实现事务管理 │
│ ├─ 实现权限控制 │
│ └─ 实现RPC框架 │
│ │
└──────────────────────────────────────────┘
12.1 动态代理的工作原理总结
┌─────────────────────────────────────────────────────┐
│ 动态代理的完整工作流程 │
├─────────────────────────────────────────────────────┤
│ │
│ 第1步:定义目标对象 │
│ ├─ 目标类(可以有接口或没有) │
│ └─ 包含真正的业务逻辑 │
│ │
│ 第2步:定义增强逻辑 │
│ ├─ JDK:实现InvocationHandler │
│ └─ CGLIB:实现MethodInterceptor │
│ │
│ 第3步:创建代理对象 │
│ ├─ JDK:Proxy.newProxyInstance() │
│ └─ CGLIB:Enhancer.create() │
│ │
│ 第4步:调用代理对象 │
│ ├─ 代理对象拦截方法调用 │
│ ├─ 执行前置增强 │
│ ├─ 调用原方法 │
│ ├─ 执行后置增强 │
│ └─ 返回结果 │
│ │
└─────────────────────────────────────────────────────┘
12.2 JDK vs CGLIB的本质区别
┌──────────────────────────────────────────┐
│ JDK和CGLIB的本质区别 │
├──────────────────────────────────────────┤
│ │
│ JDK动态代理 │
│ ├─ 原理:实现接口 │
│ ├─ 结构:代理类 implements 接口 │
│ ├─ 调用:通过接口多态 │
│ └─ 反射:每次调用都需要反射 │
│ │
│ CGLIB动态代理 │
│ ├─ 原理:继承父类 │
│ ├─ 结构:代理类 extends 目标类 │
│ ├─ 调用:通过继承多态 │
│ └─ 反射:使用MethodProxy避免反射 │
│ │
│ 关键差异 │
│ ├─ 依赖:JDK需要接口,CGLIB不需要 │
│ ├─ 性能:CGLIB调用快(避免反射) │
│ ├─ 创建:CGLIB创建慢(字节码生成) │
│ └─ 限制:CGLIB无法代理final方法 │
│ │
└──────────────────────────────────────────┘
十三、反射与动态代理的关系
13.1 反射是基础
// 动态代理依赖反射机制
// JDK动态代理:显式使用反射
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// method是反射获取的Method对象
return method.invoke(target, args); // 通过反射调用原方法
}
// CGLIB动态代理:隐式使用反射
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) {
// proxy.invokeSuper()内部也使用反射机制
return proxy.invokeSuper(obj, args);
}
// 动态代理 = 反射 + 代理模式
13.2 动态代理是反射的应用
┌──────────────────────────────────────────┐
│ 反射与动态代理的层次关系 │
├──────────────────────────────────────────┤
│ │
│ 第1层:反射机制(基础) │
│ ├─ Class对象 │
│ ├─ Method、Field、Constructor │
│ └─ 运行时获取和操作类信息 │
│ │
│ 第2层:动态代理(应用) │
│ ├─ 利用反射实现方法拦截 │
│ ├─ 在运行时生成代理类 │
│ └─ 实现横切关注点 │
│ │
│ 第3层:AOP框架(高层) │
│ ├─ Spring AOP │
│ ├─ 自动创建代理 │
│ └─ 用户无感知反射和代理 │
│ │
└──────────────────────────────────────────┘
十四、动态代理的实际应用场景
14.1 Spring AOP(面向切面编程)
// 场景:为所有Service方法添加日志和事务
@Aspect
@Component
public class TransactionAspect {
@Before("execution(* com.example.service.*.*(..))")
public void beginTransaction() {
System.out.println("开始事务");
}
@After("execution(* com.example.service.*.*(..))")
public void commitTransaction() {
System.out.println("提交事务");
}
@AfterThrowing("execution(* com.example.service.*.*(..))")
public void rollbackTransaction() {
System.out.println("回滚事务");
}
}
// Spring自动为所有Service创建代理
// 用户无需关心代理的存在
14.2 权限控制
// 场景:为某些方法添加权限检查
public class PermissionInterceptor implements InvocationHandler {
private Object target;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 检查权限
if (!hasPermission(method)) {
throw new SecurityException("无权限访问:" + method.getName());
}
// 调用原方法
return method.invoke(target, args);
}
}
// 使用
UserService userService = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
new PermissionInterceptor(new UserServiceImpl())
);
userService.delete(1); // 自动检查权限
14.3 缓存
// 场景:为查询方法添加缓存
public class CacheInterceptor implements MethodInterceptor {
private Map<String, Object> cache = new ConcurrentHashMap<>();
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
// 生成缓存key
String cacheKey = method.getName() + Arrays.toString(args);
// 检查缓存
if (cache.containsKey(cacheKey)) {
System.out.println("从缓存返回:" + cacheKey);
return cache.get(cacheKey);
}
// 调用原方法
Object result = proxy.invokeSuper(obj, args);
// 存入缓存
cache.put(cacheKey, result);
return result;
}
}
// 使用
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new CacheInterceptor());
UserService userService = (UserService) enhancer.create();
userService.getUserById(1); // 第一次调用,执行查询
userService.getUserById(1); // 第二次调用,从缓存返回
14.4 性能监控
// 场景:监控方法执行时间
public class PerformanceInterceptor implements InvocationHandler {
private Object target;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
try {
return method.invoke(target, args);
} finally {
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
System.out.println("方法:" + method.getName() +
",耗时:" + duration + "ms");
// 如果超过阈值,发送告警
if (duration > 1000) {
sendAlert("方法执行过慢:" + method.getName());
}
}
}
}
14.5 RPC框架
// 场景:实现远程方法调用
public class RpcInvocationHandler implements InvocationHandler {
private String serverAddress;
public RpcInvocationHandler(String serverAddress) {
this.serverAddress = serverAddress;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 序列化请求
RpcRequest request = new RpcRequest(
method.getDeclaringClass().getName(),
method.getName(),
method.getParameterTypes(),
args
);
// 2. 发送网络请求
RpcResponse response = sendRequest(serverAddress, request);
// 3. 反序列化返回值
return response.getResult();
}
}
// 使用
UserServiceRPC userService = (UserServiceRPC) Proxy.newProxyInstance(
UserServiceRPC.class.getClassLoader(),
new Class[]{UserServiceRPC.class},
new RpcInvocationHandler("192.168.1.100:8080")
);
// 看起来像本地调用,实际上是远程调用
User user = userService.getUserById(1);
十五、动态代理的常见问题
Q1: 为什么JDK代理需要接口,CGLIB不需要?
答:
- JDK代理通过实现接口来创建代理类
- 代理类需要实现目标接口的所有方法
- 如果没有接口,就无法创建代理类
- CGLIB通过继承来创建代理类
- 代理类继承目标类,可以重写所有方法
- 不需要接口,支持具体类
Q2: 为什么CGLIB无法代理final方法?
答:
- CGLIB通过继承实现代理
- final方法无法被重写
- 所以CGLIB无法拦截final方法的调用
public class UserService {
public final void save(User user) { // final方法
// CGLIB无法代理这个方法
}
}
Q3: 动态代理会影响性能吗?
答:
- 代理对象的创建有开销(特别是CGLIB)
- 方法调用有额外开销(反射或字节码调用)
- 但在实际应用中,这个开销通常可以接受
优化建议:
- 缓存代理对象,避免重复创建
- 避免在代理中做重操作
- 对性能敏感的代码,考虑使用MethodHandle
Q4: 如何选择JDK还是CGLIB?
答:
有接口?
├─ 是 → JDK(原生支持,无需依赖)
└─ 否 → CGLIB(支持具体类)
Spring框架?
├─ Spring Framework → JDK(默认)
└─ SpringBoot 2.x+ → CGLIB(默认)
可以通过配置修改默认选择
Q5: 代理对象和原对象是同一个吗?
答:
UserService target = new UserServiceImpl();
UserService proxy = createProxy(target);
System.out.println(target == proxy); // false,不是同一个对象
System.out.println(target instanceof UserService); // true
System.out.println(proxy instanceof UserService); // true
- 代理对象和原对象不是同一个
- 但代理对象实现了相同的接口或继承了相同的类
- 从外部看,它们是可以互换的
十六、动态代理的最佳实践
16.1 推荐做法
// 1. 优先使用框架提供的代理机制
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void beforeMethod(JoinPoint joinPoint) {
System.out.println("方法调用前");
}
}
// 2. 缓存代理对象
public class ProxyFactory {
private static final Map<Class<?>, Object> proxyCache = new ConcurrentHashMap<>();
public static <T> T getProxy(Class<T> targetClass, T target) {
return (T) proxyCache.computeIfAbsent(targetClass, key -> {
return Proxy.newProxyInstance(
targetClass.getClassLoader(),
new Class[]{targetClass},
new MyInvocationHandler(target)
);
});
}
}
// 3. 避免在代理中做重操作
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// ✅ 轻操作:日志、权限检查
System.out.println("方法调用");
// ❌ 重操作:数据库查询、网络请求
// userRepository.queryAll();
return method.invoke(target, args);
}
// 4. 使用接口编程
public interface UserService {
void save(User user);
}
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
// 业务逻辑
}
}
// 5. 异常处理
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return method.invoke(target, args);
} catch (InvocationTargetException e) {
// 处理目标方法抛出的异常
throw e.getTargetException();
} catch (IllegalAccessException e) {
// 处理访问异常
throw new RuntimeException(e);
}
}
16.2 避免做法
// ❌ 不要频繁创建代理对象
for (int i = 0; i < 1000; i++) {
UserService proxy = Proxy.newProxyInstance(...); // 每次都创建
}
// ❌ 不要在代理中做复杂业务逻辑
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// ❌ 不要在这里写业务逻辑
if (method.getName().equals("save")) {
// 复杂的业务逻辑
}
return method.invoke(target, args);
}
// ❌ 不要忽视异常处理
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// ❌ 没有异常处理
return method.invoke(target, args);
}
// ❌ 不要混用多种代理方式
// 同一个应用中,尽量统一使用一种代理方式
十七、动态代理的演进
17.1 现代Java的替代方案
// 1. MethodHandle(Java 7+)
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.findVirtual(
UserService.class,
"save",
MethodType.methodType(void.class, User.class)
);
mh.invoke(userService, user);
// 优势:性能接近直接调用,JVM可以优化
// 2. 字节码增强(ASM、Javassist、CGLIB)
// Spring使用CGLIB进行字节码增强
// 编译期生成代码,避免运行时反射成本
// 3. 编译期注解处理(APT)
// Lombok、Dagger等在编译期生成代码
// 避免运行时反射开销
17.2 Java 19+ 虚拟线程与代理
// Java 19引入虚拟线程,改变了异步编程方式
// 动态代理在虚拟线程中的应用
@Aspect
@Component
public class VirtualThreadAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
// 在虚拟线程中执行
return ForkJoinTask.adapt(() -> {
try {
return joinPoint.proceed();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}).invoke();
}
}
十八、关键要点回顾
18.1 动态代理的核心概念
| 概念 | 说明 | 用途 |
|---|---|---|
| 代理模式 | 为对象提供代理以控制访问 | 增强功能 |
| JDK代理 | 基于接口的代理 | 有接口的类 |
| CGLIB代理 | 基于继承的代理 | 无接口的类 |
| InvocationHandler | JDK代理的拦截器 | 拦截方法调用 |
| MethodInterceptor | CGLIB代理的拦截器 | 拦截方法调用 |
| AOP | 面向切面编程 | 横切关注点 |
18.2 动态代理的三个层次
第1层:基础机制
├─ JDK动态代理(反射 + 接口)
└─ CGLIB动态代理(字节码 + 继承)
第2层:应用框架
├─ Spring AOP
├─ 权限控制
├─ 缓存
└─ 性能监控
第3层:高级特性
├─ 事务管理
├─ RPC框架
├─ 日志系统
└─ 安全框架
18.3 动态代理的权衡
┌────────────────────────────────────┐
│ 动态代理的权衡分析 │
├────────────────────────────────────┤
│ │
│ 优点 │
│ ✓ 解耦业务和增强逻辑 │
│ ✓ 通用性强 │
│ ✓ 易于维护和扩展 │
│ ✓ 支持动态添加功能 │
│ │
│ 缺点 │
│ ✗ 增加代码复杂度 │
│ ✗ 性能开销(通常可接受) │
│ ✗ 调试困难(代理隐藏细节) │
│ ✗ 需要理解代理机制 │
│ │
│ 结论 │
│ → 框架层使用,业务层通过框架使用 │
│ → 理解原理,谨慎使用 │
│ → 优先使用Spring AOP等框架 │
│ │
└────────────────────────────────────┘
十九、记忆口诀
动态代理运行时,自动生成代理类
不改原始业务代码,横切关注点织入
JDK基于接口实现,CGLIB继承目标类
有接口用JDK,无接口用CGLIB
Spring默认JDK,Boot改用CGLIB
InvocationHandler和MethodInterceptor
反射是基础,代理是应用
AOP是框架,用户无感知
缓存代理对象,避免重复创建
权限日志事务,缓存性能监控
RPC框架安全,动态代理无处不在
理解原理很重要,框架使用最关键
二十、相关链接
- 01-Class对象与反射机制 - 动态代理的基础
- 注解原理 - 注解与代理的结合
- Spring AOP(面向切面编程) - 实际应用
- 设计模式-代理模式 - 理论基础(代理模式理论即本篇一~三章,体系总览见 设计模式体系)
💬 评论