Spring 的代理模式通过 JDK 动态代理和 CGLIB 动态生成代理类,实现了 AOP 的核心功能。它不仅是事务管理、缓存、安全控制等特性的基础,也是 Spring 框架灵活性和扩展性的重要体现。掌握代理模式的实现原理和最佳实践,能显著提升代码的可维护性和性能。
1. Spring 代理模式的核心特点
增强对象行为:在不修改原始类的情况下,通过代理对象增强功能(如日志、事务、缓存)。
透明性:调用方无需感知代理对象的存在,直接使用目标对象接口。
动态生成:Spring 使用 JDK 动态代理或 CGLIB 动态生成代理类。
AOP 基础:代理模式是 Spring AOP 的实现基础,用于实现横切关注点(如事务管理)。
2. Spring 版本与源码获取
下载方式:
https://www.toutiao.com/article/7477754870884336178/
调试技巧:
关键断点位置:
AbstractAutoProxyCreator.createProxy() → ProxyFactory.getProxy()。
观察 JdkDynamicAopProxy 和 CglibAopProxy 的代理生成逻辑。
3. 源码级实现剖析
代理对象像一个“中间人”,在调用目标方法前先执行额外逻辑(如安全检查、日志记录)。
// 核心类:JdkDynamicAopProxy
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 获取拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// 2. 执行拦截器链
if (chain.isEmpty()) {
return method.invoke(target, args);
} else {
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
return invocation.proceed();
}
}
}
4. 代理模式的多维度应用
代理类型对比
代理类型 | JDK 动态代理 | CGLIB 动态代理 |
实现方式 | 基于接口(Proxy + InvocationHandler) | 基于继承(生成目标类的子类) |
性能 | 较快(反射调用) | 较慢(生成字节码) |
限制 | 目标类必须实现接口 | 无法代理 final 类或方法 |
配置方式对比
XML 配置:
注解配置:
@Aspect
@Component
public class LoggingAspect {
@Around("execution(* com.example.Service.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
// 前置逻辑
Object result = joinPoint.proceed();
// 后置逻辑
return result;
}
}
5. 代理模式 vs 装饰器模式
场景 | 代理模式 | 装饰器模式 |
目的 | 控制访问(如权限校验) | 动态扩展功能(如添加日志) |
实现方式 | 运行时动态生成代理类 | 手动编写装饰器类 |
与 Spring 集成 | 无缝结合 AOP | 需手动管理装饰器链 |
6. 实际应用场景
事务管理:通过 @Transactional 注解实现声明式事务。
@Transactional
public void transferMoney(Account from, Account to, double amount) {
from.withdraw(amount);
to.deposit(amount);
}
缓存:通过 @Cacheable 注解实现方法结果缓存。
@Cacheable("users")
public User getUserById(Long id) {
return userRepository.findById(id);
}
安全控制:通过自定义切面实现权限校验。
@Around("@annotation(RequiresAdmin)")
public Object checkAdmin(ProceedingJoinPoint joinPoint) throws Throwable {
if (!currentUser.isAdmin()) {
throw new AccessDeniedException("Permission denied");
}
return joinPoint.proceed();
}
7. 源码调试与陷阱规避
调试步骤:
在 JdkDynamicAopProxy.invoke() 或 CglibAopProxy.intercept() 设置断点。
观察拦截器链的执行顺序(如事务切面、日志切面)。
常见陷阱:
- 自调用失效:目标对象内部方法调用不会经过代理(需通过 AopContext.currentProxy() 获取代理对象)。
- 循环依赖:代理对象与目标对象相互引用可能导致循环依赖(需用 @Lazy 延迟加载)。
8. 性能与最佳实践
优点:解耦业务逻辑与横切关注点。动态增强对象行为,无需修改原始代码。
优化建议:尽量减少拦截器链的长度(如合并多个切面逻辑)。对高频调用方法禁用代理(如通过 @Scope("prototype") 避免代理)。
欢迎讨论