Spring 将 Bean 元数据与 Bean 实例分离。BeanDefinition 描述如何创建对象,
而 BeanFactory 会在请求对象时应用该定义。ApplicationContext 还提供协调启动、
后处理、事件、资源加载和关闭功能。
上下文刷新期间发生的事情 #
AbstractApplicationContext.refresh() 会在应用程序单例可以使用之前准备环境和
Bean 工厂。主要阶段如下:
- 准备或获取
BeanFactory并加载 Bean 定义。 - 在定义仍可修改时调用
BeanFactoryPostProcessors,包括配置类处理。 - 发现并注册
BeanPostProcessors。 - 初始化上下文基础设施,例如消息解析、事件和上下文特定的资源。
- 实例化其余的非延迟初始化单例 Bean。
- 发布已刷新的状态和
ContextRefreshedEvent。
BeanFactoryPostProcessor 修改元数据。相比之下,BeanPostProcessor 会处理实例,
可以注入字段、调用生命周期注解,或将 Bean 替换为代理。由于普通应用程序 Bean
可能会在所有后处理器注册之前被实例化,因此从工厂后处理器中创建普通应用程序 Bean
是不安全的。
依赖顺序与 depends-on
#
容器会先创建 Bean 的构造器依赖和属性依赖,再创建依赖它的 Bean。因此,构造器注入 同时表达了必需的关系和创建顺序:
@Component
final class ReportService {
private final ReportRepository repository;
ReportService(ReportRepository repository) {
this.repository = repository;
}
}当不存在注入关系时,例如初始化共享外部资源,@DependsOn("schemaInitializer")
或 XML 中的 depends-on 可以添加顺序约束。对于单例 Bean,它还会反转销毁顺序:
依赖其他 Bean 的 Bean 会先于其依赖的 Bean 被销毁。depends-on 会隐藏类所使用的
依赖,因此不应替代实际的类型化依赖关系。
实例初始化 #
对于普通 Bean,Spring 会执行以下操作:
- 实例化对象,通常通过调用构造器或工厂方法完成。
- 解析并注入属性和字段。
- 调用 aware 回调,包括
BeanNameAware、BeanFactoryAware和ApplicationContextAware。 - 在初始化之前调用每个
BeanPostProcessor。 - 依次调用
@PostConstruct、InitializingBean.afterPropertiesSet(), 以及配置的自定义 init 方法。 - 在初始化之后调用每个
BeanPostProcessor。
Spring 基础设施通过后处理器实现该序列的部分步骤,因此自定义处理器必须遵守顺序约定。 初始化后的处理器可能返回代理,而不是原始对象。只有在整个处理链完成后,调用方才会 收到已准备就绪、可能经过增强的对象。
构造器注入是建立必需不变量最安全的位置。当初始化依赖注入的属性或构造器中不可用的
容器服务时,适合使用 @PostConstruct。
销毁与上下文关闭 #
关闭可配置的上下文会销毁受管理的单例。对于每个 Bean,Spring 会调用 @PreDestroy、
DisposableBean.destroy() 和配置的自定义 destroy 方法,但会遵循适配器的重复方法检查。
SmartLifecycle 为需要协调关闭的组件增加了分阶段的停止回调。
应用程序应关闭手动创建的上下文,或使用 registerShutdownHook() 注册关闭钩子。
Spring Boot 会在进程正常关闭的过程中关闭其应用程序上下文。进程突然终止时无法保证
回调执行,因此持久性正确性不能仅依赖这些回调。
原型的所有权 #
原型作用域意味着每次容器请求都会产生一个新实例。当原型直接注入单例时,只会在创建
单例期间解析一次;该字段会一直保留同一个原型实例。如果每次操作都需要新实例,请使用
ObjectProvider<T>、Jakarta Provider<T>、方法注入或作用域代理。
Spring 会完整初始化原型并将其返回给调用方,但不会保留该实例,也不会在之后调用销毁回调。
因此,所有权会转移给接收方。获取了持有文件、套接字或执行器的原型的代码必须显式关闭它。
如果需要集中清理,自定义 BeanPostProcessor 可以跟踪此类实例,但显式的资源所有权更清晰。
评论
在 GitHub 上阅读或评论(发表评论需要登录)