@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)的作用

@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)是 Spring 框架中用于解决作用域代理问题的关键配置,尤其在处理不同作用域的 Bean 依赖时至关重要。以下分三个层次详细解释其工作原理和使用场景:

一、核心作用原理

1. 代理模式触发条件

  • 应用场景 :当长生命周期 Bean(如 singleton) 依赖 短生命周期 Bean(如 prototype/request/session)
  • 经典问题:单例 Bean 初始化时只会注入一次原型 Bean,后续始终使用同一个实例
  • 解决方案:通过创建代理对象实现延迟获取

2. 代理机制实现

java 复制代码
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PrototypeBean {
    // 每次调用方法都会获取新实例
}
  • 代理对象:Spring 会生成一个 CGLIB 动态代理类
  • 调用过程
    1. 代理对象拦截所有方法调用
    2. 每次调用时向容器请求当前有效实例
    3. 委托给实际 Bean 实例执行方法

3. 代理模式对比

代理模式 实现方式 适用场景
TARGET_CLASS CGLIB 代理 类没有实现接口
INTERFACES JDK 动态代理 类实现了接口
NO (默认) 无代理 相同作用域依赖

二、实际应用案例

场景:购物车系统

java 复制代码
// 会话级购物车
@Scope(value = WebApplicationContext.SCOPE_SESSION, 
       proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ShoppingCart {
    private List<Item> items = new ArrayList<>();
    
    public void addItem(Item item) {
        items.add(item);
    }
}

// 单例服务类
@Service
public class OrderService {
    private final ShoppingCart cart;  // 注入的是代理对象
    
    public OrderService(ShoppingCart cart) {
        this.cart = cart;
    }
    
    public void checkout() {
        // 每次调用时通过代理获取当前用户的购物车
        cart.clear(); 
    }
}

运行时行为:

  1. 用户A访问系统:
    • 创建用户A的 ShoppingCart 实例
    • OrderService 持有代理对象
  2. 用户B访问系统:
    • 创建用户B的 ShoppingCart 实例
    • 代理自动路由到对应实例

三、深度技术细节

1. 代理对象结构

java 复制代码
// 生成的代理类伪代码
public class ShoppingCart$$EnhancerBySpringCGLIB extends ShoppingCart {
    private TargetSource targetSource;  // 目标源(负责获取实际Bean)
    
    public void addItem(Item item) {
        // 从TargetSource获取当前实际对象
        ShoppingCart target = (ShoppingCart) targetSource.getTarget();
        target.addItem(item);
    }
}

2. 生命周期示意图

sequenceDiagram participant Client participant Proxy participant SpringContainer participant RealBean Client->>Proxy: 调用方法 Proxy->>SpringContainer: 请求当前Bean实例 SpringContainer->>RealBean: 创建新实例(prototype) SpringContainer-->>Proxy: 返回新实例 Proxy->>RealBean: 委托调用 RealBean-->>Proxy: 返回结果 Proxy-->>Client: 返回结果

3. 使用限制与注意事项

  1. 类设计约束

    • 被代理类不能是 final
    • 被代理方法不能是 final 方法
    • 需要引入 CGLIB 依赖(Spring Boot 已自动包含)
  2. 性能影响

    • 每次方法调用都需要与容器交互
    • 建议仅在实际需要时使用
  3. 调试技巧

    java 复制代码
    // 检查是否为代理对象
    boolean isProxy = AopUtils.isCglibProxy(bean);
    // 获取目标类
    Class<?> targetClass = AopUtils.getTargetClass(bean);

四、常见问题解决方案

问题1:代理导致的事务失效

现象@Transactional 注解不生效
原因 :代理对象未正确继承父类方法
解决方案

java 复制代码
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
@Transactional // 注解必须加在类或接口上
public class TransactionalBean {
    // 方法实现
}

问题2:循环依赖异常

现象 :启动时报 BeanCurrentlyInCreationException
解决方案

java 复制代码
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
@Lazy // 结合延迟初始化
public class CircularDependencyBean {
    // 类实现
}

五、最佳实践建议

  1. 作用域选择策略

    • 优先考虑 request/session 作用域
    • 谨慎使用 prototype(容易导致内存泄漏)
  2. 代理模式选择原则

    java 复制代码
    // 有接口时优先使用接口代理
    @Scope(proxyMode = ScopedProxyMode.INTERFACES)
    public class InterfaceBasedBean implements MyInterface {
        // 实现
    }
  3. 性能优化方案

    java 复制代码
    @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE) // 标记为基础设施Bean
    public class HighPerformanceBean {
        // 实现
    }

通过合理使用作用域代理,开发者可以在保持 Spring Bean 管理便利性的同时,灵活处理各种复杂的作用域依赖场景。理解代理机制的工作原理对于调试复杂依赖关系和优化系统性能具有重要意义。

相关推荐
XiaoLeisj2 小时前
【设计模式】深入解析代理模式(委托模式):代理模式思想、静态模式和动态模式定义与区别、静态代理模式代码实现
java·spring boot·后端·spring·设计模式·代理模式·委托模式
pwzs3 小时前
常见的 Spring Boot 注解汇总
java·spring boot·后端·spring
非ban必选4 小时前
spring-ai使用Document存储至milvus的数据结构
前端·spring·milvus
无业哥5 小时前
Mac搭建Spring5源码环境
spring
Chase_______7 小时前
Java后端开发——分层解耦详解
java·开发语言·spring·web
ApeAssistant8 小时前
Spring + 设计模式 (十八) 行为型 - 责任链模式
spring·设计模式
陈平安Java and C9 小时前
OpenFeign和Gateway
spring
写bug写bug9 小时前
Spring事务失效的9大场景,你踩过几个?
java·后端·spring
ApeAssistant9 小时前
Spring + 设计模式 (十六) 行为型 - 状态模式
spring·设计模式
ApeAssistant9 小时前
Spring + 设计模式 (十五) 行为型 - 模板方法模式
spring·设计模式