优秀的编程知识分享平台

网站首页 > 技术文章 正文

二十八、Spring 中的代理模式深度解析

nanyue 2025-03-10 19:00:07 技术文章 1 ℃

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") 避免代理)。

欢迎讨论

最近发表
标签列表