Spring Bean是Spring框架的核心,理解它的生命周期 和作用域是面试必考知识点,也是实际开发中避坑的关键。本文用大白话+代码示例,带你轻松掌握这两大核心问题!
一、Spring Bean的生命周期:从出生到销毁
Spring Bean的生命周期可以概括为四大阶段 :实例化 → 属性赋值 → 初始化 → 销毁。但细节远不止这些,我们通过代码一步步拆解:
1. 实例化(Instantiate)
Spring通过反射调用Bean的构造方法 ,创建一个"原始对象"(此时对象还未填充属性)。
相当于:工厂里造了一辆车的骨架,但还没有装发动机和轮胎。
csharp
public class Car {
public Car() {
System.out.println("1. 调用构造方法:创建Car实例");
}
}
2. 属性赋值(Populate Properties)
Spring通过依赖注入(DI) 为Bean的属性赋值(如@Autowired
、@Value
)。
相当于:给车装上发动机、轮胎等零件。
kotlin
public class Car {
@Autowired
private Engine engine; // 依赖注入引擎
public void setEngine(Engine engine) {
System.out.println("2. 属性赋值:注入Engine");
this.engine = engine;
}
}
3. 初始化(Initialize)
这一步是Bean的"成人礼",Spring提供了多种扩展点:
- BeanPostProcessor接口:在初始化前后插入自定义逻辑。
- @PostConstruct注解:标记初始化完成后执行的方法。
- InitializingBean接口 :实现
afterPropertiesSet()
方法。
typescript
public class Car implements InitializingBean {
@PostConstruct
public void init() {
System.out.println("3.1 @PostConstruct:初始化方法");
}
@Override
public void afterPropertiesSet() {
System.out.println("3.2 InitializingBean:属性赋值后执行");
}
}
4. 销毁(Destroy)
Bean使用完毕后,Spring会调用销毁方法(仅适用于单例Bean)。
- @PreDestroy注解:标记销毁前执行的方法。
- DisposableBean接口 :实现
destroy()
方法。
typescript
public class Car implements DisposableBean {
@PreDestroy
public void preDestroy() {
System.out.println("4.1 @PreDestroy:销毁前执行");
}
@Override
public void destroy() {
System.out.println("4.2 DisposableBean:销毁方法");
}
}
完整生命周期流程图
less
构造方法 → @Autowired注入 → BeanPostProcessor前置处理
→ @PostConstruct → InitializingBean →
BeanPostProcessor后置处理 → ...(使用中)
→ @PreDestroy → DisposableBean
二、Spring Bean的作用域:单例还是原型?
Spring Bean的作用域决定了Bean的创建方式和生命周期,常用作用域有两种:
1. 单例(Singleton)
-
特点:整个Spring容器中,Bean只有一个实例(默认作用域)。
-
适用场景:无状态的工具类、服务类(如Service、DAO)。
-
配置方式:
less@Scope("singleton") @Service public class UserService { ... }
2. 原型(Prototype)
-
特点:每次请求Bean时,都会创建一个新实例。
-
适用场景:需要保持状态的Bean(如用户购物车)。
-
配置方式:
less@Scope("prototype") @Component public class ShoppingCart { ... }
其他作用域(Web应用专用)
作用域 | 描述 |
---|---|
request | 一次HTTP请求创建一个Bean |
session | 一次用户会话创建一个Bean |
application | 整个Web应用共享一个Bean |
三、常见问题及避坑指南
问题1:单例Bean中注入原型Bean会失效?
现象 :单例Bean中注入的原型Bean只会初始化一次。
原因 :单例Bean在初始化时已经完成依赖注入,后续请求不会重新创建原型Bean。
解决方案 :使用@Lookup
注解或ApplicationContext.getBean()
手动获取原型Bean。
问题2:@PostConstruct和InitializingBean的执行顺序?
答案 :@PostConstruct
先于InitializingBean
执行。
建议 :优先使用@PostConstruct
,代码更简洁。
问题3:Bean的生命周期能定制吗?
能! 通过实现以下接口:
BeanPostProcessor
:干预所有Bean的初始化过程。BeanFactoryPostProcessor
:修改Bean的定义信息。
总结
- 生命周期 :记住四大阶段(实例化 → 属性注入 → 初始化 → 销毁),善用
@PostConstruct
和@PreDestroy
。 - 作用域:单例省资源,原型保状态,Web作用域按需选。
- 避坑:单例中注入原型Bean需特殊处理,避免状态污染。