@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 管理便利性的同时,灵活处理各种复杂的作用域依赖场景。理解代理机制的工作原理对于调试复杂依赖关系和优化系统性能具有重要意义。

相关推荐
电商数据girl14 分钟前
淘宝/天猫获得淘宝商品评论 API 返回值说明
java·大数据·开发语言·数据库·人工智能·spring
菲兹园长1 小时前
Spring IOC(五个类注解)
java·python·spring
wuyunhang1234562 小时前
应用分层简介
spring boot·spring·servlet·maven
张小娟2 小时前
springCloud使用webSocket(接收端)
websocket·spring·spring cloud
都叫我大帅哥3 小时前
深入理解Spring中的@ComponentScan注解:原理、实战与避坑指南
spring
都叫我大帅哥4 小时前
Spring框架@ComponentScan注解终极指南:从基础到高阶实战
spring
都叫我大帅哥4 小时前
Spring框架注解Bean深度解析:原理、用法、案例与最佳实践
spring
都叫我大帅哥4 小时前
Spring依赖注入注解详解:@Autowired、@Resource、@Inject
spring
都叫我大帅哥4 小时前
导致默认扫描失效的6种典型场景
spring