题目
Spring Bean的作用域有哪些?如何在Web应用中正确使用request和session作用域?
信息
- 类型:问答
- 难度:⭐⭐
考点
Bean作用域理解,作用域配置方式,Web作用域实践,作用域陷阱规避
快速回答
Spring支持5种标准Bean作用域:
- singleton:默认作用域,每个容器一个实例
- prototype:每次注入创建新实例
- request:每个HTTP请求一个实例(Web)
- session:每个用户会话一个实例(Web)
- 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:通过
RequestContextListener或RequestContextFilter将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>最佳实践
- 作用域选择原则:
- 无状态服务使用
singleton - 需要维护状态的Web对象(如表单数据)用
request - 用户会话数据(如购物车)用
session
- 无状态服务使用
- 代理模式配置:
ScopedProxyMode.TARGET_CLASS:CGLIB代理(类代理)ScopedProxyMode.INTERFACES:JDK动态代理(接口代理)
- 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作用域依赖代理会有轻微性能开销