侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

Spring Bean的作用域有哪些?如何在Web应用中正确使用request和session作用域?

2025-12-5 / 0 评论 / 4 阅读

题目

Spring Bean的作用域有哪些?如何在Web应用中正确使用request和session作用域?

信息

  • 类型:问答
  • 难度:⭐⭐

考点

Bean作用域理解,作用域配置方式,Web作用域实践,作用域陷阱规避

快速回答

Spring支持5种标准Bean作用域:

  1. singleton:默认作用域,每个容器一个实例
  2. prototype:每次注入创建新实例
  3. request:每个HTTP请求一个实例(Web)
  4. session:每个用户会话一个实例(Web)
  5. application:整个ServletContext生命周期一个实例(Web)

配置方式:

  • XML:<bean scope="request">
  • 注解:@Scope("request")
  • Java配置:@Bean @Scope(value = WebApplicationContext.SCOPE_REQUEST)
## 解析

原理说明

Spring Bean作用域由org.springframework.beans.factory.config.Scope接口实现,核心机制:

  • singleton:Bean实例存储在IoC容器缓存中,所有依赖注入共享同一实例
  • prototype:每次getBean()或依赖注入时调用BeanFactory.createBean()创建新实例
  • request/session:通过RequestContextListenerRequestContextFilter将HTTP请求绑定到当前线程,使用ServletRequestAttributes存储Bean

代码示例

配置示例:

// 注解配置
@Controller
public class UserController {
    @Autowired
    @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
    private UserPreferences userPrefs; // 每个请求独立实例
}

// Java配置
@Configuration
public class AppConfig {
    @Bean
    @Scope(scopeName = "session", proxyMode = ScopedProxyMode.INTERFACES)
    public ShoppingCart cart() {
        return new ShoppingCart();
    }
}

XML配置:

<bean id="loginTracker" class="com.example.LoginTracker"
      scope="session">
    <aop:scoped-proxy/>  <!-- 启用代理 -->
</bean>

最佳实践

  1. 作用域选择原则
    • 无状态服务使用singleton
    • 需要维护状态的Web对象(如表单数据)用request
    • 用户会话数据(如购物车)用session
  2. 代理模式配置
    • ScopedProxyMode.TARGET_CLASS:CGLIB代理(类代理)
    • ScopedProxyMode.INTERFACES:JDK动态代理(接口代理)
  3. Web环境必备:在web.xml中添加:
    <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>

常见错误

  • 错误1:在非Web环境使用request/session作用域
    → 解决方案:确保spring-web依赖存在且配置Web上下文
  • 错误2:单例Bean直接注入短作用域Bean
    → 错误代码:
    @Service // singleton
    public class OrderService {
        @Autowired
        private ShoppingCart cart; // session-scoped,注入的是初始实例!
    }
    → 正确做法:通过代理注入(如@Scope(proxyMode=...)
  • 错误3:在多线程中修改singleton Bean的非线程安全字段→ 需同步或使用ThreadLocal

扩展知识

  • 自定义作用域:实现Scope接口并注册:
    // 实现Scope接口
    public class ThreadScope implements Scope { ... }
    
    // 注册作用域
    context.getBeanFactory().registerScope("thread", new ThreadScope());
  • 作用域销毁回调:实现DisposableBean或使用@PreDestroy,Spring会在作用域结束时自动调用
  • 性能影响:prototype作用域频繁创建可能增加GC压力,request/session作用域依赖代理会有轻微性能开销