Spring bean 的生命周期、注入方式和作用域
一、Spring Bean的生命周期
Spring Bean的生命周期是指从Bean的定义加载到最终销毁的整个过程,Spring框架在每个阶段都提供了钩子方法,允许开发者在特定时机执行自定义逻辑。
1. Bean定义加载阶段
容器启动时加载配置(XML/注解/JavaConfig),解析@Component、@Bean等注解或XML配置,生成BeanDefinition。BeanDefinition是描述Bean的元数据对象,包含类名、作用域、初始化方法、属性值、依赖关系等信息。
2. Bean实例化阶段
Spring容器根据BeanDefinition创建Bean实例,通常通过反射机制实现。例如,对于UserService
类,Spring会使用类似Class.forName("com.example.UserService").newInstance()
的方式创建对象实例。
3. 属性填充阶段
在实例化之后,Spring会将依赖的其他Bean注入到当前Bean中。例如,如果UserService
依赖于UserRepository
,Spring会查找并将UserRepository
的实例注入到UserService
中。
4. 初始化回调阶段
Bean可以实现InitializingBean
接口,在afterPropertiesSet
方法中编写初始化逻辑。也可以通过以下方式实现初始化回调:
public class ExampleBean implements InitializingBean {public void afterPropertiesSet() {// 初始化逻辑}
}
或使用@PostConstruct
注解标记初始化方法420。
5. 销毁阶段
在容器关闭时,Spring会调用实现了DisposableBean
接口的Bean的destroy()
方法,或使用@PreDestroy
注解标记的方法执行清理工作。
二、Spring Bean的注入方式
Spring支持多种依赖注入方式,每种方式有其适用场景和优缺点。
1. 构造器注入(Constructor Injection)
通过类的构造函数注入依赖,是Spring官方推荐的方式,尤其是在Spring 4.3及以上版本中。
特点:
- 不可变性:依赖项在对象创建时注入,之后不可更改
- 强依赖:明确表示类的依赖关系
- 易于测试:便于单元测试
- 避免循环依赖:如果存在循环依赖,Spring会抛出异常
示例代码:
@Service
public class UserService {private final UserRepository userRepository;@Autowiredpublic UserService(UserRepository userRepository) {this.userRepository = userRepository;}
}
2. Setter注入(Setter Injection)
通过setter方法注入依赖,适用于可选依赖或需要在对象创建后进行依赖设置的情况。
特点:
- 灵活性高:支持可选依赖
- 解决循环依赖:Spring容器可处理Setter注入的循环依赖
- 向后兼容:适合逐步迁移旧代码到依赖注入模式
示例代码:
@Service
public class OrderService {private PaymentProcessor paymentProcessor;@Autowiredpublic void setPaymentProcessor(PaymentProcessor paymentProcessor) {this.paymentProcessor = paymentProcessor;}
}
3. 字段注入(Field Injection)
直接在类的字段上使用注解注入依赖,虽然代码简洁但不被推荐。
特点:
- 代码简洁
- 可能导致循环依赖
- 不利于单元测试
- 无法使用final修饰符
4. 方法注入(Method Injection)
一种特殊的注入方式,主要用于解决单例Bean中需要原型Bean的情况。
实现方式:
- 实现
ApplicationContextAware
接口,手动获取Bean - 使用
@Lookup
注解标记方法
示例代码:
@Component
public class CommandManager implements ApplicationContextAware {private ApplicationContext applicationContext;public Object process(Map<String, Object> params) {Command command = createCommand();command.setStateMap(params);return command.execute();}protected Command createCommand() {return applicationContext.getBean(Command.class);}
}
三、Spring Bean的作用域
Spring Bean的作用域决定了Bean实例的创建方式和生命周期管理。
1. 标准作用域
作用域 | 描述 | 适用场景 |
---|---|---|
singleton | 整个容器中只有一个实例 | 无状态Bean,Spring默认作用域 |
prototype | 每次请求都创建新实例 | 有状态Bean |
2. Web相关作用域
作用域 | 描述 | 适用场景 |
---|---|---|
request | 每个HTTP请求创建一个新实例 | Web请求处理 |
session | 每个用户会话创建一个实例 | 用户会话数据 |
application | ServletContext生命周期 | 全局应用数据 |
3. 自定义作用域
Spring允许开发者创建自定义作用域,例如线程级作用域或多租户系统中的租户级作用域22。
实现步骤:
- 实现
Scope
接口 - 注册自定义作用域到容器
- 在Bean定义中使用自定义作用域
4. 作用域与生命周期的关系
不同作用域的Bean在生命周期管理上有显著差异:
- singleton Bean:在容器启动时创建,容器关闭时销毁
- prototype Bean:每次请求时创建,由调用者负责销毁
- request/session Bean:在请求/会话开始时创建,在请求/会话结束时销毁
四、最佳实践与最新特性(2025)
1. 注入方式选择
- 强制依赖:优先使用构造器注入
- 可选依赖:考虑使用Setter注入
- 避免使用:字段注入,特别是在新项目中
2. 生命周期管理
- 使用
@PostConstruct
和@PreDestroy
注解管理初始化和销毁逻辑,而不是实现InitializingBean
和DisposableBean
接口 - 对于持有外部资源(如数据库连接)的Bean,务必实现销毁逻辑
3. 作用域选择
- 默认使用singleton作用域
- 有状态对象使用prototype作用域
- Web相关数据使用request/session作用域