Bean 的作用域与生命周期
一、Bean 的 6 种作用域
┌─────────────────────────────────────────────────────────────────┐
│ Spring Bean Scopes │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ singleton │
│ │ │ ────────────────── │
│ │ ┌───┐ │ 整个容器只有一个实例 │
│ │ │ A │ │ 默认作用域,Spring 默认使用 │
│ │ └───┘ │ │
│ └─────────────┘ │
│ │
│ ┌─────────────┐ prototype │
│ │ ┌───┐ │ ────────────────── │
│ │ │ A │ │ 每次注入创建新实例 │
│ │ └───┘ │ │
│ └─────────────┘ ┌─────────────┐ │
│ │ ┌───┐ │ │
│ │ │ A'│ │ │
│ │ └───┘ │ │
│ └─────────────┘ │
│ │
│ ┌─────────────┐ request (Web) │
│ │ ┌───┐ │ ────────────────── │
│ │ │ A │ │ 每次 HTTP 请求创建新实例 │
│ │ └───┘ │ │
│ └─────────────┘ │
│ │
│ ┌─────────────┐ session (Web) │
│ │ ┌───┐ │ ────────────────── │
│ │ │ A │ │ 每次 HTTP 会话创建新实例 │
│ │ └───┘ │ │
│ └─────────────┘ │
│ │
│ ┌─────────────┐ application (Web) │
│ │ │ ────────────────── │
│ │ ┌───┐ │ ServletContext 生命周期内单例 │
│ │ └───┘ │ │
│ └─────────────┘ │
│ │
│ ┌─────────────┐ websocket (Web) │
│ │ ┌───┐ │ ────────────────── │
│ │ └───┘ │ WebSocket 生命周期内单例 │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘二、作用域详解
1. singleton(单例)
java
@Service
public class UserService {
// 默认单例,整个容器只有一个实例
}
@Configuration
public class AppConfig {
@Bean
@Scope("singleton") // 默认值
public UserService userService() {
return new UserService();
}
}特点:
- 容器启动时创建(饿加载)或第一次使用时创建(懒加载)
- 整个容器共享一个实例
- 适合无状态的 Bean
线程安全问题:
java
@Service
public class UnsafeService {
private int count = 0; // 共享变量,多线程不安全!
public void increment() {
count++; // 非原子操作
}
}
// 解决方案:使用 ThreadLocal
@Service
public class SafeService {
private final ThreadLocal<Integer> count = ThreadLocal.withInitial(() -> 0);
public void increment() {
count.set(count.get() + 1);
}
}
// 或使用 prototype(每次创建新实例)
@Service
@Scope("prototype")
public class PrototypeService {
private int count = 0; // 每个线程有自己的实例
}2. prototype(原型)
java
@Service
@Scope("prototype")
public class OrderService {
private final String orderId = UUID.randomUUID().toString();
public String getOrderId() {
return orderId;
}
}
// 测试
OrderService o1 = ctx.getBean(OrderService.class);
OrderService o2 = ctx.getBean(OrderService.class);
o1.getOrderId() != o2.getOrderId(); // true特点:
- 每次注入或 getBean() 创建新实例
- Spring 不管理原型 Bean 的生命周期
- 适合有状态的 Bean
生命周期差异:
java
@Service
@Scope("prototype")
public class PrototypeBean {
public PrototypeBean() {
System.out.println("PrototypeBean 创建");
}
@PreDestroy
public void destroy() {
System.out.println("PrototypeBean 销毁"); // 不会调用!
}
}
// 单例 Bean 中注入原型 Bean
@Service
public class SingletonBean {
private final PrototypeBean prototypeBean;
public SingletonBean(PrototypeBean prototypeBean) {
this.prototypeBean = prototypeBean; // 注入时创建一次
}
public PrototypeBean getPrototype() {
return prototypeBean; // 永远是同一个!
}
// 正确做法:使用 ObjectFactory 延迟获取
@Autowired
private ObjectFactory<PrototypeBean> prototypeFactory;
public PrototypeBean getPrototypeBean() {
return prototypeFactory.getObject(); // 每次获取新实例
}
}3. request(请求域)
java
@Controller
@RequestScope
public class RequestController {
private final Map<String, Object> requestAttrs = new HashMap<>();
public void setAttribute(String key, Object value) {
requestAttrs.put(key, value);
}
}
// 或 XML 配置
<bean id="requestBean" class="com.example.RequestBean" scope="request">
<aop:scoped-proxy /> <!-- 代理以便注入到单例 -->
</bean>特点:
- 每个 HTTP 请求有独立的 Bean 实例
- 请求结束实例销毁
- 需要 Web 环境支持
4. session(会话域)
java
@Controller
@SessionScope
public class UserSessionController {
private User currentUser;
public void setCurrentUser(User user) {
this.currentUser = user;
}
public User getCurrentUser() {
return currentUser;
}
}特点:
- 每个 HTTP 会话有独立的 Bean 实例
- 会话超时或失效时销毁
- 适合存储用户登录信息
5. application(应用域)
java
@Service
@ApplicationScope
public class AppStatistics {
private final AtomicInteger visitCount = new AtomicInteger();
public void increment() {
visitCount.incrementAndGet();
}
public int getCount() {
return visitCount.get();
}
}特点:
- ServletContext 生命周期内单例
- 整个应用共享
- 与普通 singleton 的区别:singleton 是 Spring 容器的单例
6. websocket(WebSocket 域)
java
@Component
@Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class WebSocketSessionBean {
private WebSocket session;
public void setSession(WebSocket session) {
this.session = session;
}
}三、自定义作用域
实现 Scope 接口
java
public class ThreadScope implements Scope {
private final ThreadLocal<Map<String, Object>> threadLocal =
new ThreadLocal<>() {
@Override
protected Map<String, Object> initialValue() {
return new HashMap<>();
}
};
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String, Object> scope = threadLocal.get();
if (!scope.containsKey(name)) {
scope.put(name, objectFactory.getObject());
}
return scope.get(name);
}
@Override
public Object remove(String name) {
Map<String, Object> scope = threadLocal.get();
return scope.remove(name);
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
// 线程结束时调用
}
@Override
public Object resolveContextualObject(String key) {
return null;
}
@Override
public String getConversationId() {
return Thread.currentThread().getName();
}
}注册自定义作用域
java
@Configuration
public class ScopeConfig {
@Bean
public CustomScopeConfigurer customScopeConfigurer() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("thread", new ThreadScope());
return configurer;
}
}
// 使用
@Service
@Scope("thread")
public class ThreadScopedService {
// 每个线程有独立的实例
}四、Bean 的生命周期
完整生命周期流程
┌─────────────────────────────────────────────────────────────────────┐
│ Bean 生命周期 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 实例化 (Instantiation) │
│ └─▶ new UserService() ← 构造函数调用 │
│ │
│ 2. 属性填充 (Populate) │
│ └─▶ @Autowired 依赖注入 │
│ └─▶ setXxx() 属性 setter 调用 │
│ │
│ 3. 初始化前 (Initialization - Aware) │
│ └─▶ BeanNameAware.setBeanName() │
│ └─▶ BeanFactoryAware.setBeanFactory() │
│ └─▶ ApplicationContextAware.setApplicationContext() │
│ │
│ 4. 初始化 (Initialization) │
│ └─▶ @PostConstruct 标注的方法 │
│ └─▶ InitializingBean.afterPropertiesSet() │
│ └─▶ @Bean(initMethod = "init") │
│ │
│ 5. BeanPostProcessor.postProcessAfterInitialization() │
│ │
│ ════════════════════════════════════════════════════════════════ │
│ │
│ [ Bean 就绪,可以使用 ] │
│ │
│ ════════════════════════════════════════════════════════════════ │
│ │
│ 6. 销毁前 (Destruction - Aware) │
│ └─▶ @PreDestroy 标注的方法 │
│ └─▶ DisposableBean.destroy() │
│ └─▶ @Bean(destroyMethod = "cleanup") │
│ │
│ 7. 销毁 (Destruction) │
│ └─▶ 自定义销毁逻辑 │
│ │
└─────────────────────────────────────────────────────────────────────┘各阶段代码示例
java
@Service
public class UserService implements BeanNameAware,
BeanFactoryAware,
ApplicationContextAware,
InitializingBean,
DisposableBean {
private String beanName;
private BeanFactory beanFactory;
private ApplicationContext applicationContext;
// ==================== Aware 回调 ====================
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("[Aware] BeanName: " + name);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
System.out.println("[Aware] BeanFactory: " + beanFactory.getClass());
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
System.out.println("[Aware] ApplicationContext: " + applicationContext);
}
// ==================== 初始化 ====================
@PostConstruct
public void postConstruct() {
System.out.println("[@PostConstruct] 初始化后");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("[InitializingBean] afterPropertiesSet()");
}
public void init() {
System.out.println("[@Bean(initMethod)] init()");
}
// ==================== 销毁 ====================
@PreDestroy
public void preDestroy() {
System.out.println("[@PreDestroy] 销毁前");
}
@Override
public void destroy() throws Exception {
System.out.println("[DisposableBean] destroy()");
}
public void cleanup() {
System.out.println("[@Bean(destroyMethod)] cleanup()");
}
}XML 配置方式
xml
<bean id="userService"
class="com.example.UserService"
init-method="init"
destroy-method="cleanup">
<property name="userDao" ref="userDao"/>
</bean>五、BeanPostProcessor 后置处理器
执行时机
┌─────────────────────────────────────────────────────────┐
│ BeanPostProcessor 流程 │
├─────────────────────────────────────────────────────────┤
│ │
│ new UserService() ← 实例化 │
│ │ │
│ ▼ │
│ populateBean() ← 属性填充(@Autowired) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────┐ │
│ │ postProcessBeforeInitialization() │ │
│ │ │ │
│ │ Aware回调 → @PostConstruct → initMethod │ │
│ │ │ │
│ │ postProcessAfterInitialization() │ │
│ └─────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 就绪状态 Bean 使用 │
│ │ │
│ ▼ │
│ 容器关闭 → destroy() → @PreDestroy │
│ │
└─────────────────────────────────────────────────────────┘内置后置处理器
java
// 1. AutowiredAnnotationBeanPostProcessor
// 处理 @Autowired, @Value, @Inject
// 2. CommonAnnotationBeanPostProcessor
// 处理 @PostConstruct, @PreDestroy, @Resource
// 3. RequiredAnnotationBeanPostProcessor
// 处理 @Required(已废弃)
// 4. AnnotationAwareAspectJAutoProxyCreator
// 处理 AOP 自动代理
// 5. ApplicationContextAwareProcessor
// 处理各种 Aware 接口自定义后置处理器
java
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof UserService) {
System.out.println("[Before] " + beanName + " 初始化前");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof UserService) {
System.out.println("[After] " + beanName + " 初始化后");
}
return bean;
}
}应用:生成代理对象
java
@Component
public class InjectProxyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof UserService) {
return createProxy((UserService) bean);
}
return bean;
}
private UserService createProxy(UserService target) {
return (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
System.out.println("代理逻辑 before");
Object result = method.invoke(target, args);
System.out.println("代理逻辑 after");
return result;
}
);
}
}六、初始化与销毁顺序
多 Bean 依赖情况
java
public class BeanA {
@PostConstruct
public void init() { System.out.println("A init"); }
}
public class BeanB {
@PostConstruct
public void init() { System.out.println("B init"); }
}
public class BeanC {
private BeanA beanA;
public BeanC(BeanA beanA) { this.beanA = beanA; } // 构造注入
@PostConstruct
public void init() { System.out.println("C init"); }
}
// 依赖顺序决定初始化顺序
BeanC(BeanA) → 先创建BeanA → 再创建BeanC@Order 控制同类型 Bean 顺序
java
@Service
@Order(2)
public class OneService { }
@Service
@Order(1) // 数字越小越先执行
public class TwoService { }
// 或使用 Priority
@Service
@Priority(1)
public class PriorityService { }@DependsOn 显式依赖
java
@Service
@DependsOn({"beanA", "beanB"})
public class MyService {
// 确保 beanA, beanB 先初始化
}七、延迟初始化 Bean
@Lazy 注解
java
@Service
@Lazy // 第一次使用时才创建
public class LazyService {
public LazyService() {
System.out.println("LazyService 创建");
}
}全局懒加载
yaml
# Spring Boot 2.x
spring.main.lazy-initialization: true
# Spring Boot 3.x
spring.main.include-lazy-init: true优点:
- 加快应用启动速度
- 减少内存占用(按需加载)
缺点:
- 第一次请求响应慢
- 错误延迟暴露
八、面试高频问题
Q1: Bean 生命周期有哪些阶段?
- 实例化(构造函数)
- 属性填充(依赖注入)
- Aware 回调(BeanName、BeanFactory 等)
- 初始化(@PostConstruct、InitializingBean)
- BeanPostProcessor 后置处理
- 就绪状态
- 销毁(@PreDestroy、DisposableBean)
Q2: @PostConstruct vs InitializingBean vs @Bean(initMethod) 区别?
| 方式 | 来源 | 执行时机 | 推荐度 |
|---|---|---|---|
| @PostConstruct | JSR-250 | 构造器 + 属性填充后 | ⭐⭐⭐⭐⭐ |
| InitializingBean | Spring | afterPropertiesSet | ⭐⭐⭐ |
| @Bean(initMethod) | Spring | 同上 | ⭐⭐⭐⭐ |
执行顺序:
@PostConstruct → InitializingBean.afterPropertiesSet() → @Bean(initMethod)Q3: singleton Bean 如何注入 prototype Bean?
- ObjectFactory 方式(推荐)
java
@Autowired
private ObjectFactory<PrototypeBean> prototypeFactory;
public PrototypeBean getPrototype() {
return prototypeFactory.getObject();
}- Provider 方式(标准 JSR330)
java
@Inject
private Provider<PrototypeBean> prototypeProvider;
public PrototypeBean getPrototype() {
return prototypeProvider.get();
}- @Scope + proxyMode
java
@Bean
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public PrototypeBean prototypeBean() {
return new PrototypeBean();
}Q4: Spring 如何解决循环依赖?
- 构造器循环依赖:无法解决,启动失败
- setter 循环依赖:通过三级缓存解决
- 一级缓存:成品 Bean
- 二级缓存:早期引用
- 三级缓存:ObjectFactory
Q5: 什么是三级缓存?
java
// DefaultSingletonBeanRegistry 源码简化
public class DefaultSingletonBeanRegistry {
// 一级缓存:完全初始化好的 Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
// 二级缓存:早期暴露的 Bean(未完成属性填充和方法调用)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
// 三级缓存:Bean 工厂,用于创建早期引用
private final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>();
}九、下一章预告
下一章我们将学习 SpringBoot 快速开发:
- Spring Boot 入门与自动配置原理
- starter 起步依赖
- YAML 配置文件详解
- 常用注解与条件装配