Spring容器初始化过程和Bean生命周期探究
# 一、前言
作为Java开发者,你肯定遇到过这样的问题:为什么有时候@Autowired
注入的Bean是null?为什么有些Bean没有按预期的顺序初始化?这些问题的根源往往在于对Spring容器初始化过程和Bean生命周期的理解不够深入。
Spring容器就像一个精密的工厂,它不仅要生产出各种Bean,还要管理它们的整个生命历程。从出生(实例化)到成长(初始化)再到最终的消亡(销毁),每个环节都有着严格的规则和扩展点。
理解这套机制不仅能让你写出更可靠的代码,还能在出现问题时快速定位原因。本文将带你深入Spring的"生产车间",看看容器是如何启动的,Bean又是如何一步步"成长"的。
# 二、容器基础
想要理解Spring容器,我们先得知道它的基本组成部分。就像要理解一家工厂是怎么运作的,得先了解车间、生产线这些基础设施一样。
# 1、- 最基础的容器
BeanFactory
是Spring的"祖师爷",它定义了最基本的容器功能。你可以把它想象成一个最简单的工厂,主要负责:
- 生产Bean:根据配置信息,用反射创建Bean实例
- 管理依赖:自动把Bean需要的依赖注入进去
- 控制生命周期:决定Bean什么时候创建,什么时候销毁
- 作用域管理:单例的Bean只创建一次,原型的Bean每次都新建
不过说实话,直接使用BeanFactory
的场景不多,它更像是一个接口规范。在实际项目中,我们用得更多的是它的"升级版"——ApplicationContext
。
# 2、- 功能齐全的容器
ApplicationContext
才是我们日常开发的主角,它在BeanFactory
的基础上增加了很多实用功能:
- 事件发布:可以发布和监听应用事件,比如容器启动完成事件
- 国际化支持:支持多语言消息,让应用国际化变得简单
- 资源加载:可以加载各种资源文件,如配置文件、图片等
- 环境抽象:统一管理不同环境的配置,如dev、test、prod
- 层级结构:支持父子容器,在复杂应用中很有用
简单来说,如果BeanFactory
是毛坯房,那ApplicationContext
就是精装修,该有的都有了。这也是为什么我们在Spring Boot项目中直接使用的就是它。
# 3、Aware接口 - Bean获取容器资源的后门
有时候,你的Bean需要知道一些容器的信息,比如自己的名字、所在的容器等等。Spring提供了一堆以Aware
结尾的接口来满足这些需求,就像给Bean开了个"后门",让它们能拿到容器的一些内部信息。
常用的几个接口:
ApplicationContextAware
:最常用的,能拿到整个应用上下文BeanNameAware
:让Bean知道自己在容器中的名字EnvironmentAware
:获取环境配置,比如配置文件中的属性ResourceLoaderAware
:获取资源加载器,用来加载文件等资源
这些接口的用法都很简单,实现接口,Spring在初始化Bean时会自动调用对应的set方法。举个例子:
@Component
public class MyService implements ApplicationContextAware {
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.context = applicationContext;
}
public void dynamicGetBean() {
// 现在可以动态获取其他Bean了
UserService userService = context.getBean(UserService.class);
userService.processUser();
}
}
# 三、容器启动流程
当你启动一个Spring应用时,Spring容器经历了一个复杂的"开机"过程。这个过程的核心就是refresh()
方法,它就像是启动按钮,一旦按下,整个容器就开始运转起来。
我们先看一个简单的启动示例:
public class Application {
public static void main(String[] args) {
// 创建容器
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext();
// 注册配置类
context.register(AppConfig.class);
// 这一步是关键 - 启动容器
context.refresh();
// 现在可以使用Bean了
UserService userService = context.getBean(UserService.class);
userService.processUser();
// 别忘了关闭容器
context.close();
}
}
# 1、refresh()方法 - 容器启动的十二个步骤
这个方法就是容器启动的"总导演",它按照固定的顺序执行了12个关键步骤:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 第1步:准备工作 - 记录启动时间,设置标志位
prepareRefresh();
// 第2步:获取BeanFactory - 这是Bean的生产车间
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 第3步:配置BeanFactory - 设置类加载器、添加后置处理器
prepareBeanFactory(beanFactory);
try {
// 第4步:子类可以对BeanFactory进行后置处理
postProcessBeanFactory(beanFactory);
// 第5步:执行BeanFactory后置处理器 - 修改Bean定义
invokeBeanFactoryPostProcessors(beanFactory);
// 第6步:注册Bean后置处理器 - 拦截Bean创建过程
registerBeanPostProcessors(beanFactory);
// 第7步:初始化消息源 - 国际化支持
initMessageSource();
// 第8步:初始化事件广播器 - 事件发布功能
initApplicationEventMulticaster();
// 第9步:子类可以初始化特殊Bean - 如Web容器的Servlet
onRefresh();
// 第10步:注册事件监听器
registerListeners();
// 第11步:实例化所有非懒加载的单例Bean - 这是重点!
finishBeanFactoryInitialization(beanFactory);
// 第12步:完成刷新 - 发布容器刷新完成事件
finishRefresh();
} catch (BeansException ex) {
// 出错了就清理现场
destroyBeans();
cancelRefresh(ex);
throw ex;
} finally {
// 清理缓存
resetCommonCaches();
}
}
}
其中最关键的两个概念你必须要理解:
- BeanFactoryPostProcessor:在Bean实例化之前,修改Bean的定义信息。比如修改Bean的属性值、作用域等
- BeanPostProcessor:在Bean实例化之后,对Bean进行处理。比如创建代理对象、注入依赖等
这两个处理器就是Spring扩展能力的核心,后面我们会详细介绍。
# 四、Bean的一生
一个Bean从诞生到消亡,就像人的一生一样,经历了不同的阶段。理解这个过程,能帮你解决很多实际开发中的问题。
# 1、Bean生命周期的完整流程
想象Bean的成长过程:
1. 出生阶段
- 实例化:通过构造函数创建Bean对象
- 属性填充:通过
@Autowired
等方式注入依赖
2. 成长阶段
- Aware接口回调:让Bean知道自己的身份信息
BeanNameAware
:告诉Bean自己叫什么名字ApplicationContextAware
:告诉Bean自己在哪个容器里
- BeanPostProcessor前置处理:类似"入学体检"
- 初始化方法调用:
@PostConstruct
注解的方法InitializingBean.afterPropertiesSet()
- 自定义的
init-method
- BeanPostProcessor后置处理:类似"毕业典礼"
3. 工作阶段
- Bean正式投入使用,处理业务逻辑
4. 退休阶段(容器关闭时)
@PreDestroy
注解的方法DisposableBean.destroy()
- 自定义的
destroy-method
这个流程看起来复杂,但记住关键的几个节点就够了:属性注入 → Aware接口 → 初始化方法 → 可以使用 → 销毁方法。
# 2、生命周期扩展点
Spring在Bean的生命周期中提供了很多扩展点,让你可以在合适的时机插入自己的逻辑:
容器级别的扩展点:
BeanFactoryPostProcessor
:修改Bean定义,比如修改属性值、添加新的Bean定义BeanPostProcessor
:拦截Bean创建过程,可以创建代理、添加功能等
Bean级别的扩展点:
Aware
接口:让Bean获取容器信息InitializingBean
/@PostConstruct
:Bean初始化时执行逻辑DisposableBean
/@PreDestroy
:Bean销毁时清理资源Lifecycle
接口:控制Bean的启动和停止
实际开发中,最常用的就是@PostConstruct
和@PreDestroy
注解,简单方便。
# 五、- 容器启动时的"总工程师"
BeanFactoryPostProcessor
是Spring提供的一个非常强大的扩展点,它能在Bean实例化之前修改Bean的定义信息。就像工厂的总工程师,在生产线开始生产之前,可以修改生产图纸。
# 1、Spring内置的BeanFactoryPostProcessor
Spring自带了几个常用的处理器:
ConfigurationClassPostProcessor
:处理@Configuration
类,解析@Bean
、@ComponentScan
等注解PropertySourcesPlaceholderConfigurer
:处理${}
占位符,用配置文件的值替换AutowiredAnnotationBeanPostProcessor
:处理@Autowired
注解
其中最重要的是ConfigurationClassPostProcessor
,它负责处理基于注解的配置。
# 2、实战案例:MyBatis如何注册Mapper
我们来看一个经典的例子:MyBatis是如何把Mapper接口注册到Spring容器中的。你有没有想过,为什么一个接口可以直接@Autowired
注入使用?
MyBatis提供了MapperScannerConfigurer
,它实现了BeanDefinitionRegistryPostProcessor
接口:
@Component
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 扫描指定包下的Mapper接口
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setBasePackage("com.example.mapper");
// 为每个Mapper接口创建BeanDefinition
// 并设置FactoryBean为MapperFactoryBean
scanner.scan();
}
}
这个过程的巧妙之处在于:
- 扫描阶段:找到所有Mapper接口
- 注册阶段:为每个接口创建一个
BeanDefinition
- 代理创建:使用
MapperFactoryBean
创建接口的代理实现
这样,原本不能实例化的接口,就变成了可以注入的Bean。这就是BeanFactoryPostProcessor
的威力。
上面提到的BeanDefinitionRegistryPostProcessor
是BeanFactoryPostProcessor
的增强版,它不仅能修改Bean定义,还能向容器中添加新的Bean定义。
执行顺序是:BeanDefinitionRegistryPostProcessor
→ BeanFactoryPostProcessor
→ Bean实例化
这样设计的好处是,先注册新的Bean定义,再统一处理所有Bean定义,最后才开始实例化。
# 3、- 特殊的Bean工厂
FactoryBean
是Spring的一个特殊接口,它不是普通的Bean,而是专门用来创建其他Bean的工厂。当Spring遇到实现了FactoryBean
接口的类时,它会调用该工厂的getObject()
方法来获取真正的Bean。
public class UserFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
// 复杂的创建逻辑
User user = new User();
user.setName("Generated User");
return user;
}
@Override
public Class<?> getObjectType() {
return User.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
这种机制特别适用于创建复杂对象,比如MyBatis的MapperFactoryBean
就是用来创建Mapper接口的代理对象的。
简单来说,ClassPathBeanDefinitionScanner
就是Spring用来扫描类路径的工具,它能找到所有带@Component
等注解的类,并把它们注册成Bean定义。
BeanDefinition
则是Bean的"身份证",记录了Bean的所有信息:类名、作用域、属性值、依赖关系等。Spring就是根据这些信息来创建Bean实例的。
# 六、- Bean创建过程的“参谋”
如果说BeanFactoryPostProcessor
是在Bean实例化前修改定义,那BeanPostProcessor
就是在Bean实例化后进行加工。它就像是一个“参谋”,在Bean的创建过程中提供建议和修改。
# 1、Spring内置的BeanPostProcessor
Spring自带了很多实用的处理器:
AutowiredAnnotationBeanPostProcessor
:处理@Autowired
、@Value
注觢CommonAnnotationBeanPostProcessor
:处理@PostConstruct
、@PreDestroy
、@Resource
注觢ApplicationListenerDetector
:检测事件监听器并自动注册AsyncAnnotationBeanPostProcessor
:处理@Async
注觢
这些处理器就是为什么你在Bean中使用@Autowired
、@PostConstruct
等注觢就能起作用的原因。
# 2、自定义BeanPostProcessor
你也可以写自己的BeanPostProcessor
,来实现一些特殊的需求。比如记录Bean的创建日志:
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean.getClass().isAnnotationPresent(Service.class)) {
System.out.println("Service Bean [" + beanName + "] 即将初始化");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean.getClass().isAnnotationPresent(Service.class)) {
System.out.println("Service Bean [" + beanName + "] 初始化完成");
}
return bean;
}
}
这样,所有的Service Bean在初始化前后都会打印日志。这对于调试和监控Bean的创建过程很有帮助。
# 七、初始化和销毁方法
在实际开发中,我们经常需要在Bean初始化时做一些准备工作,比如加载配置、初始化连接池等;在Bean销毁时做一些清理工作,比如关闭连接、释放资源等。
# 1、使用注解方式(推荐)
最简单的方式就是使用@PostConstruct
和@PreDestroy
注觢:
@Service
public class UserService {
@PostConstruct
public void init() {
System.out.println("初始化用户服务,加载配置文件...");
// 初始化逻辑
}
@PreDestroy
public void cleanup() {
System.out.println("清理用户服务资源...");
// 清理逻辑
}
public void processUser() {
System.out.println("处理用户业务");
}
}
# 2、使用接口方式
如果你不喜欢注觢,也可以实现接口:
@Service
public class DataService implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("数据服务初始化完成");
}
@Override
public void destroy() throws Exception {
System.out.println("数据服务销毁");
}
}
注意:这些方法只对单例模式的Bean有效。对于原型模式(prototype)的Bean,Spring不会管理其销毁,所以@PreDestroy
不会被调用。
# 八、Lifecycle接口 - 控制Bean的启停
有时候,我们需要更精细地控制Bean的启动和停止行为,比如后台任务、线程池、网络服务等。这时候就可以使用Lifecycle
接口。
# 1、实现示例
来看一个后台任务的例子:
@Component
public class BackgroundTask implements Lifecycle {
private boolean running = false;
@Override
public void start() {
if (!running) {
System.out.println("BackgroundTask started!");
running = true;
// 启动后台任务
new Thread(this::runTask).start();
}
}
@Override
public void stop() {
if (running) {
System.out.println("BackgroundTask stopped!");
running = false;
// 停止后台任务
}
}
@Override
public boolean isRunning() {
return running;
}
private void runTask() {
while (running) {
System.out.println("BackgroundTask is running...");
try {
Thread.sleep(1000); // 模拟任务执行
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
}
这样,当你调用context.start()
时,后台任务就会启动;调用context.stop()
时,任务就会停止。
# 2、Lifecycle扩展接口
# 2.1、SmartLifecycle
SmartLifecycle
是 Lifecycle
的扩展接口,提供了更丰富的生命周期控制功能,例如:
- 自动启动:在 Spring 容器启动时自动调用
start()
方法。 - 阶段控制:通过
getPhase()
方法定义组件的启动和停止顺序。
# a、示例代码
import org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;
@Component
public class SmartBackgroundTask implements SmartLifecycle {
private boolean running = false;
@Override
public void start() {
if (!running) {
System.out.println("SmartBackgroundTask started!");
running = true;
new Thread(this::runTask).start();
}
}
@Override
public void stop() {
if (running) {
System.out.println("SmartBackgroundTask stopped!");
running = false;
}
}
@Override
public boolean isRunning() {
return running;
}
@Override
public boolean isAutoStartup() {
return true; // 自动启动
}
@Override
public void stop(Runnable callback) {
stop();
callback.run(); // 通知容器停止完成
}
@Override
public int getPhase() {
return 0; // 定义启动和停止的顺序
}
private void runTask() {
while (running) {
System.out.println("SmartBackgroundTask is running...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
}
# 2.2、Phased接口
Phased
接口定义了 getPhase()
方法,用于控制组件的启动和停止顺序。SmartLifecycle
已经继承了 Phased
。
# 3、Lifecycle与其他生命周期方式的区别
Lifecycle
:用于显式控制组件的启动和停止行为,通常用于需要长时间运行的任务或资源管理。@PostConstruct
和@PreDestroy
:用于在 Bean 初始化和销毁时执行特定的方法,通常用于简单的初始化和清理操作。
# 九、总结
通过本文的探索,我们深入了解了Spring容器的初始化过程和Bean的生命周期。虽然这些机制看起来复杂,但理解它们的核心原理后,你会发现Spring的设计其实非常优雅。
掌握了这些知识,你就能:
- 在Bean注入失败时快速定位问题
- 灵活运用各种扩展点来实现自定义功能
- 更好地理解Spring Boot的自动配置原理
- 写出更健壮、更可靠的Spring应用
记住,Spring的核心思想就是控制反转和依赖注入,而容器和Bean生命周期机制则是这一思想的完美体现。希望这篇文章能帮助你在Spring开发的道路上走得更远!
祝你变得更强!