本文专注讲解 Spring Boot 全生命周期的17大核心扩展接口、真实执行顺序、核心作用、可运行实战代码,重点补充每个接口“能拿到什么资源、怎么落地使用”,贴合真实开发场景(如配置中心、AOP、优雅停机),同时补充面试高频考点,从入门到精通,一篇覆盖所有核心扩展点,再也不用零散查资料!
前言
在 Spring Boot 开发中,我们很少直接手写底层扩展接口,但你每天用的 Spring Boot 自动配置、MyBatis 整合、Nacos/Apollo 配置中心、AOP 事务、优雅停机 等核心功能,底层全靠这些扩展接口实现。
作为后端开发者,掌握这17大扩展点,能帮你:
- ✅ 看懂框架底层源码(比如 Nacos 如何注入远程配置、MyBatis 如何扫描 Mapper 接口);
- ✅ 实现自定义组件(比如自定义 Starter、动态注册 Bean、全局缓存预热);
- ✅ 解决生产环境问题(比如启动加载顺序、Bean 依赖冲突、资源泄漏);
- ✅ 面试轻松加分(Spring Boot 生命周期+扩展点,是中高级后端必考题)。
本文按 Spring Boot 真实启动+销毁顺序,整理17大核心扩展点,每个扩展点都包含:执行时机、核心作用、能拿到什么资源、可直接运行的实战代码、真实使用场景、触发执行方式,保姆级教程,新手复制就能跑,老手可直接用于实战。
补充说明:所有代码均基于 Spring Boot 2.7.x(最常用、最稳定版本),兼容 Spring Boot 3.x(注:3.x 注册扩展点的配置文件路径有细微差异,文中已标注)。
一、Spring Boot 全生命周期总览(极简版)
先记住整体流程,后面逐个接口详解(每个接口对应流程中的一个节点),核心顺序不可逆:
启动早期(环境准备)→ 容器初始化 → 工厂后置处理 → Bean 定义加载 → Bean 实例化 → Bean 初始化 → AOP 增强 → 容器就绪 → 运行中 → 容器销毁(优雅停机)
关键提醒:前2个扩展点(EnvironmentPostProcessor、ApplicationEnvironmentPreparedEvent)是 Spring Boot 独有的启动最早期扩展,比传统 Spring 扩展点(如 ApplicationContextInitializer)更早,也是配置中心的核心实现方式。
二、17大核心扩展点(按真实执行顺序,含实战代码)
重点:每个扩展点都标注了「执行时机」「核心作用」「实战场景」,代码可直接复制到项目中运行,同时补充「触发方式」,明确“实现后如何让 Spring 调用”,避免“写了代码不生效”。
第0阶段:Spring Boot 最早期(环境准备,比容器还早)
这2个扩展点是 Spring Boot 独有,用于环境初始化、配置注入,是 Nacos、Apollo 等配置中心的底层核心,也是你重点关注的扩展点。
1. EnvironmentPostProcessor(Spring 官方标准扩展)
核心定位:Environment 环境定制顶级扩展口,整个 Spring Boot 启动链路最早执行,专门用于修改、增强 Environment。
执行时机:SpringApplication.run() 刚执行 → Environment 已创建 → ApplicationContext(容器)创建之前。
能拿到什么:ConfigurableEnvironment(可配置环境)、SpringApplication(应用实例),能添加/覆盖/修改配置源,优先级可高于配置文件。
实战场景:配置中心底层(Nacos/Apollo 拉取远程配置注入 Environment)、动态配置注入、配置加解密、多环境配置优先级控制。
实战代码(可直接运行) :
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import java.util.HashMap;
import java.util.Map;
/**
* 官方标准扩展:修改 Environment,模拟配置中心注入配置
*/
public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment env, SpringApplication application) {
// 1. 构造自定义配置(模拟从 Nacos 拉取的远程配置)
Map<String, Object> customConfig = new HashMap<>();
customConfig.put("app.datasource.url", "jdbc:mysql://from-post-processor:3306/mydb");
customConfig.put("app.datasource.username", "custom-user");
customConfig.put("app.datasource.backup-urls[0]", "jdbc:mysql://backup-custom:3306/mydb");
customConfig.put("my.app.name", "spring-boot-lifecycle-demo");
// 2. 封装成 PropertySource(配置源),指定名称(用于区分配置来源)
PropertySource<?> propertySource = new MapPropertySource("customProcessorConfig", customConfig);
// 3. 添加到 Environment(addFirst 表示优先级最高,覆盖配置文件中的同名配置)
env.getPropertySources().addFirst(propertySource);
// 验证配置是否注入成功
String appName = env.getProperty("my.app.name");
System.out.println("EnvironmentPostProcessor 注入配置:my.app.name = " + appName);
}
}
触发执行方式:必须手动注册(SPI 方式),Spring Boot 会自动扫描注册的扩展类:
- Spring Boot 2.x:在
src/main/resources/META-INF/spring.factories中配置:org.springframework.boot.env.EnvironmentPostProcessor=\ ``com.example.MyEnvironmentPostProcessor - Spring Boot 3.x+:在
src/main/resources/META-INF/spring/org.springframework.boot.env.EnvironmentPostProcessor.imports中配置(仅需写全限定类名):com.example.MyEnvironmentPostProcessor
补充说明:这是 Spring 官方推荐的“修改 Environment”的扩展方式,Spring Cloud、Nacos 等框架均采用此方式注入配置。
2. ApplicationEnvironmentPreparedEvent(环境就绪事件)
核心定位:Environment 已准备完成的事件监听,执行时机略晚于 EnvironmentPostProcessor,但仍早于所有容器初始化操作。
执行时机:Environment 准备完毕 → ApplicationContext 还未创建、未刷新 → 未加载任何 Bean 定义。
能拿到什么:ConfigurableEnvironment(环境对象),可修改环境配置、激活 Profile、添加配置源,功能与 EnvironmentPostProcessor 类似,更偏向“事件监听”。
实战场景:监听环境就绪、动态插入配置源、启动日志打印、激活指定环境(dev/prod)、配置校验。
实战代码(可直接运行) :
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import java.util.HashMap;
import java.util.Map;
/**
* 最早时机:通过事件向 Environment 写入自定义配置,监听环境就绪
*/
public class MyEnvironmentConfigListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
// 拿到核心资源:ConfigurableEnvironment
ConfigurableEnvironment env = event.getEnvironment();
// 1. 构造自定义配置(模拟从配置中心拉取)
Map<String, Object> customConfig = new HashMap<>();
customConfig.put("app.datasource.url", "jdbc:mysql://custom-db:3306/mydb");
customConfig.put("app.datasource.username", "custom-user");
customConfig.put("my.app.version", "1.0.0");
// 2. 封装成 PropertySource,添加到 Environment(优先级仅次于 EnvironmentPostProcessor)
PropertySource<?> propertySource = new MapPropertySource("custom-env-config", customConfig);
env.getPropertySources().addAfter("customProcessorConfig", propertySource);
// 3. 激活 dev 环境(可动态切换环境)
env.setActiveProfiles("dev");
System.out.println("ApplicationEnvironmentPreparedEvent 激活环境:dev");
// 验证配置
String version = env.getProperty("my.app.version");
System.out.println("ApplicationEnvironmentPreparedEvent 注入配置:my.app.version = " + version);
}
}
触发执行方式:与 EnvironmentPostProcessor 注册方式一致,二选一即可:
- Spring Boot 2.x:在
spring.factories中配置:org.springframework.context.ApplicationListener=\ ``com.example.MyEnvironmentConfigListener - Spring Boot 3.x+:在
src/main/resources/META-INF/spring/org.springframework.context.ApplicationListener.imports中配置:com.example.MyEnvironmentConfigListener
关键结论:通过以上两个扩展口塞进 Environment 的配置,和写在 application.yml 里完全等价,@Value、@ConfigurationProperties、environment.getProperty() 都能正常读取,且优先级可通过 addFirst 控制(代码注入 > 配置文件)。
第1阶段:ApplicationContext 初始化前(容器启动准备)
3. ApplicationContextInitializer(容器刷新前扩展)
核心定位:容器刷新前的环境/上下文定制,传统 Spring 最早的扩展点,执行时机晚于前2个 Spring Boot 独有扩展。
执行时机:ConfigurableApplicationContext 实例创建完成后 → 调用context.refresh()(容器刷新)之前,此时上下文未初始化,Bean 定义未加载。
能拿到什么:ConfigurableApplicationContext(可配置上下文)、Environment(环境对象),能操作环境配置、上下文参数。
实战场景:启动时加载配置中心配置、激活指定环境、添加自定义配置源、上下文参数设置。
实战代码(可直接运行) :
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.MapPropertySource;
import java.util.HashMap;
import java.util.Map;
/**
* 实战用途:容器刷新前加载自定义配置、激活环境,模拟配置中心拉取配置
*/
public class CustomApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
// 1. 拿到环境对象(核心资源),验证前两个扩展口注入的配置
ConfigurableEnvironment environment = context.getEnvironment();
String appName = environment.getProperty("my.app.name");
System.out.println("ApplicationContextInitializer 读取配置:my.app.name = " + appName);
// 2. 拿到上下文,设置上下文ID(用于日志、监控)
context.setId("my-spring-boot-container");
System.out.println("ApplicationContextInitializer 设置上下文ID:" + context.getId());
// 3. 补充配置(进一步完善环境配置)
Map<String, Object> configMap = new HashMap<>();
configMap.put("app.description", "Spring Boot 17大扩展点实战");
environment.getPropertySources().addLast(new MapPropertySource("contextConfig", configMap));
}
}
触发执行方式:需要手动注册,4种方式,推荐前2种:
- Spring Boot 2.x:
spring.factories配置(推荐,自动加载):org.springframework.context.ApplicationContextInitializer=\ ``com.example.CustomApplicationContextInitializer - Spring Boot 3.x+:
src/main/resources/META-INF/spring/org.springframework.context.ApplicationContextInitializer.imports配置:com.example.CustomApplicationContextInitializer - application.yml/application.properties 配置:
context: `` initializer: ``classes: com.example.CustomApplicationContextInitializer - 编程式注册(main 方法中手动添加):
public static void main(String[] args) { `` SpringApplication app = new SpringApplication(MyApplication.class); `` app.addInitializers(new CustomApplicationContextInitializer()); `` app.run(args); ``}
第2阶段:ApplicationContext 已创建、未刷新(容器初始化)
4. ApplicationPreparedEvent(上下文就绪事件)
核心定位:ApplicationContext 已创建,但未执行 refresh() 方法,Bean 定义未加载,用于容器初始化前的最后一次配置。
执行时机:ApplicationContext 创建完成 → 容器刷新(refresh)之前 → Bean 定义未加载、Bean 未实例化。
能拿到什么:ConfigurableApplicationContext(上下文)、SpringApplication(应用实例),能对上下文进行最终配置。
实战场景:容器初始化前的最终校验、上下文参数调整、自定义 Bean 定义扫描路径设置。
实战代码(可直接运行) :
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
/**
* 实战用途:上下文已创建、未刷新前,进行最终配置和校验
*/
public class CustomApplicationPreparedListener implements ApplicationListener<ApplicationPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationPreparedEvent event) {
// 拿到核心资源:可配置上下文
ConfigurableApplicationContext context = event.getApplicationContext();
System.out.println("ApplicationPreparedEvent:上下文已创建,ID:" + context.getId());
// 实战:校验环境配置(确保核心配置已注入)
ConfigurableEnvironment env = context.getEnvironment();
String datasourceUrl = env.getProperty("app.datasource.url");
if (datasourceUrl == null || datasourceUrl.isEmpty()) {
throw new RuntimeException("核心配置 app.datasource.url 未注入,请检查配置中心!");
}
System.out.println("ApplicationPreparedEvent:核心配置校验通过,数据源URL:" + datasourceUrl);
// 实战:设置上下文的资源加载器(可选)
context.setResourceLoader(null);
}
}
触发执行方式:与 ApplicationEnvironmentPreparedEvent 一致,注册到对应的 imports 文件或 spring.factories 中即可。
第3阶段:容器刷新(Spring 原生核心扩展,Bean 相关)
这一阶段是 Spring 容器的核心,所有 Bean 的定义、实例化、初始化都在这一阶段完成,也是日常开发中最常用的扩展阶段。
5. BeanDefinitionRegistryPostProcessor(Bean 定义注册扩展)
核心定位:Bean 定义注册阶段的扩展,可动态注册、修改、删除 Bean 定义(此时 Bean 未实例化)。
执行时机:加载默认 Bean 定义后 → BeanFactoryPostProcessor 执行前,所有 Bean 仅完成“定义”,未实例化。
能拿到什么:BeanDefinitionRegistry(Bean 定义注册器)、ConfigurableListableBeanFactory(Bean 工厂),能操作 Bean 定义。
实战场景:动态扫描包注册 Bean(如 MyBatis @MapperScan 底层)、替换框架默认 Bean、条件注册 Bean、动态添加 Bean 定义。
实战代码(可直接运行) :
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;
/**
* 实战用途:动态注册 Bean,模拟 MyBatis 扫描 Mapper 接口的底层逻辑
*/
@Component // 交给 Spring 管理,自动执行
public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 1. 拿到 Bean 定义注册器(核心资源),判断某个 Bean 是否已注册
boolean hasUserService = registry.containsBeanDefinition("userService");
System.out.println("BeanDefinitionRegistryPostProcessor:userService 是否已注册:" + hasUserService);
// 2. 动态注册一个 Bean(模拟扫描到的 Mapper 接口/自定义组件)
// 构建 Bean 定义(指定 Bean 类型、作用域等)
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserService.class);
builder.setScope("singleton"); // 单例模式
builder.addPropertyValue("username", "admin"); // 设置属性
// 注册 Bean,Bean 名称为 "customUserService"
registry.registerBeanDefinition("customUserService", builder.getBeanDefinition());
System.out.println("BeanDefinitionRegistryPostProcessor:动态注册 customUserService 成功");
}
// 父接口方法,可用于修改 Bean 工厂配置(可选实现)
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 拿到 Bean 工厂,可修改 Bean 定义的属性(如懒加载)
beanFactory.getBeanDefinition("customUserService").setLazyInit(false);
System.out.println("BeanDefinitionRegistryPostProcessor:修改 customUserService 懒加载为 false");
}
// 模拟自定义组件(被动态注册的 Bean)
public static class UserService {
private String username;
public void setUsername(String username) {
this.username = username;
}
public void sayHello() {
System.out.println("Hello, " + username);
}
}
}
触发执行方式:无需手动注册,给实现类添加 @Component 注解(或其他 Spring 组件注解,如 @Service),Spring 自动扫描并执行。
6. BeanFactoryPostProcessor(Bean 工厂后置处理)
核心定位:Bean 工厂的后置处理,用于修改 Bean 定义的配置(无实例化,仅操作配置)。
执行时机:所有 Bean 定义已加载 → 所有 Bean 未实例化,执行在 BeanDefinitionRegistryPostProcessor 之后。
能拿到什么:ConfigurableListableBeanFactory(Bean 工厂),能获取所有 Bean 定义、修改 Bean 定义的属性(如懒加载、作用域)。
实战场景:替换配置文件占位符 ${}、修改 Bean 的作用域/懒加载、调整 Bean 属性值、统一配置 Bean 前缀。
注意:不能在这里实例化 Bean(比如调用 beanFactory.getBean()),会导致 Bean 提前初始化,破坏生命周期顺序。
实战代码(可直接运行) :
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.stereotype.Component;
/**
* 实战用途:修改 Bean 定义属性、替换占位符,统一配置 Bean 行为
*/
@Component
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 1. 拿到 Bean 工厂(核心资源),获取所有 Bean 定义名称
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
System.out.println("BeanFactoryPostProcessor:当前容器中 Bean 定义数量:" + beanDefinitionNames.length);
// 2. 遍历所有 Bean 定义,修改指定 Bean 的属性(实战:统一设置所有 Service 为非懒加载)
for (String beanName : beanDefinitionNames) {
if (beanName.endsWith("Service")) { // 匹配所有 Service 结尾的 Bean
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
// 修改懒加载为 false(立即初始化)
beanDefinition.setLazyInit(false);
System.out.println("BeanFactoryPostProcessor:修改 " + beanName + " 懒加载为 false");
}
}
// 3. 模拟替换占位符(实战:动态替换配置文件中的 ${app.username})
BeanDefinition userServiceDef = beanFactory.getBeanDefinition("customUserService");
String username = (String) userServiceDef.getPropertyValues().getPropertyValue("username").getValue();
// 替换占位符(模拟从配置文件读取)
userServiceDef.getPropertyValues().addPropertyValue("username", username.replace("admin", "root"));
System.out.println("BeanFactoryPostProcessor:替换 customUserService 的 username 为 root");
}
}
触发执行方式:与 BeanDefinitionRegistryPostProcessor 一致,无需手动注册,添加@Component 注解即可。
补充说明:Spring 内置的 PropertyPlaceholderConfigurer(处理 ${} 占位符)、ConfigurationClassPostProcessor(处理 @Configuration),本质都是 BeanFactoryPostProcessor。
7. InstantiationAwareBeanPostProcessor(Bean 实例化拦截)
核心定位:Bean 实例化前/后、依赖注入前的最底层拦截器,Spring 最强大的 Bean 扩展接口之一。
执行时机:Bean 实例化之前 → 实例化之后 → 依赖注入之前,执行在 Bean 工厂后置处理之后。
能拿到什么:Bean 类型、Bean 名称、Bean 实例(实例化后)、BeanDefinition,能控制实例化过程、替换 Bean 实例。
实战场景:自定义 Bean 实例化(如替代默认构造方法)、实现单例/多例外的作用域(如请求域)、跳过 Bean 实例化、AOP 底层依赖(获取 Bean 实例进行代理)。
实战代码(可直接运行) :
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.stereotype.Component;
/**
* 实战用途:控制 Bean 实例化过程、替换 Bean 实例,模拟 AOP 底层代理前置操作
*/
@Component
public class CustomInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
// 1. Bean 实例化之前执行(最核心方法),可返回自定义实例,跳过默认实例化
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
// 只拦截 customUserService 这个 Bean
if ("customUserService".equals(beanName)) {
System.out.println("InstantiationAwareBeanPostProcessor:Bean 实例化前:" + beanName + ",Bean 类型:" + beanClass.getName());
// 可选:返回自定义实例,跳过 Spring 默认的实例化(实战:自定义实例化逻辑)
// return new CustomBeanDefinitionRegistryPostProcessor.UserService();
}
return null; // 返回 null,走 Spring 默认实例化
}
// 2. Bean 实例化之后、依赖注入之前执行,可修改 Bean 实例
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if ("customUserService".equals(beanName)) {
System.out.println("InstantiationAwareBeanPostProcessor:Bean 实例化后:" + beanName + ",实例对象:" + bean);
// 返回 true:允许依赖注入;返回 false:跳过依赖注入
return true;
}
return true;
}
// 3. 依赖注入之前执行,可修改注入的属性值
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
if ("customUserService".equals(beanName)) {
System.out.println("InstantiationAwareBeanPostProcessor:依赖注入前:修改 " + beanName + " 的属性");
// 可修改注入的属性(比如动态设置属性值)
// pvs = new MutablePropertyValues(pvs).addPropertyValue("username", "test");
}
return pvs;
}
}
触发执行方式:无需手动注册,添加 @Component 注解,Spring 自动扫描,在 Bean 实例化阶段自动回调该接口的方法。
补充说明:Spring AOP 的 AnnotationAwareAspectJAutoProxyCreator 底层就是继承该接口,在实例化后、依赖注入前对 Bean 进行代理,这是 AOP 实现的核心步骤。
8. Aware 系列接口(Bean 感知容器)
核心定位:让 Bean 感知容器自身信息,获得容器的核心能力,是日常开发中最常用的扩展方式之一。
执行时机:Bean 实例化后 → 填充属性(依赖注入)完成 → 初始化方法(@PostConstruct)之前。
能拿到什么(核心3个接口):
- BeanNameAware:拿到当前 Bean 的名称(String 类型);
- BeanFactoryAware:拿到 BeanFactory(Bean 工厂),可手动获取容器内的 Bean;
- ApplicationContextAware:拿到 ApplicationContext(上下文),拥有比 BeanFactory 更丰富的功能(如事件发布、资源加载)。
实战场景:在自定义 Bean 中手动获取其他 Bean、加载资源文件、发布事件、获取环境配置。
实战代码(可直接运行) :
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* 实战用途:在自定义 Bean 中获取容器资源、手动获取其他 Bean,日常开发最常用
*/
@Component("myAwareBean")
public class CustomAwareBean implements BeanNameAware, BeanFactoryAware, ApplicationContextAware {
// 1. 从 BeanNameAware 拿到 Bean 名称
private String beanName;
// 2. 从 BeanFactoryAware 拿到 Bean 工厂
private BeanFactory beanFactory;
// 3. 从 ApplicationContextAware 拿到上下文
private ApplicationContext applicationContext;
// BeanNameAware 方法:设置 Bean 名称
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("Aware 接口:从 BeanNameAware 拿到 Bean 名称:" + beanName);
}
// BeanFactoryAware 方法:设置 Bean 工厂
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
System.out.println("Aware 接口:从 BeanFactoryAware 拿到 Bean 工厂:" + beanFactory);
// 实战:手动获取容器中的 customUserService Bean
CustomBeanDefinitionRegistryPostProcessor.UserService userService =
beanFactory.getBean(CustomBeanDefinitionRegistryPostProcessor.UserService.class);
userService.sayHello();
}
// ApplicationContextAware 方法:设置上下文
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
System.out.println("Aware 接口:从 ApplicationContextAware 拿到上下文:" + applicationContext);
// 实战:获取环境配置(上下文比 BeanFactory 功能更全)
String appName = applicationContext.getEnvironment().getProperty("my.app.name");
System.out.println("Aware 接口:从上下文拿到 app.name:" + appName);
// 实战:发布事件(上下文独有功能)
applicationContext.publishEvent(new CustomEvent(this, "Aware Bean 初始化完成"));
}
// 模拟自定义事件(上下文发布事件示例)
public static class CustomEvent extends org.springframework.context.ApplicationEvent {
private String message;
public CustomEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
// 事件监听器(接收上面发布的事件)
@Component
public static class CustomEventListener implements org.springframework.context.ApplicationListener<CustomEvent> {
@Override
public void onApplicationEvent(CustomEvent event) {
System.out.println("Aware 接口:收到事件:" + event.getMessage());
}
}
}
触发执行方式:无需任何手动注册,只要实现该系列接口的 Bean 是由 Spring 容器管理的(如添加 @Component、@Bean 注解),Spring 在创建该 Bean 时,会自动调用对应接口的方法。
9. @PostConstruct(最常用的 Bean 初始化方法)
核心定位:Bean 初始化阶段,最常用、最简洁的初始化方法(JSR-250 规范,非 Spring 原生,但 Spring 完全支持)。
执行时机:依赖注入完成后 → InitializingBean 接口方法执行前。
能拿到什么:当前 Bean 的所有注入属性、容器资源(如果实现了 Aware 接口),能执行 Bean 的初始化业务逻辑。
实战场景:加载初始化数据、初始化数据库连接、启动定时任务、初始化缓存,日常开发 80% 的初始化场景用它就够。
实战代码(可直接运行) :
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
/**
* 实战用途:Bean 初始化业务逻辑,日常开发最常用
*/
@Component
public class UserDao {
// 依赖注入(模拟数据库连接池)
@Autowired
private DataSource dataSource;
// 初始化缓存(模拟业务场景)
private List<String> userCache;
// 初始化方法:依赖注入完成后执行
@PostConstruct
public void init() {
System.out.println("@PostConstruct:UserDao 初始化:依赖注入的数据源:" + dataSource);
// 实战:初始化缓存(从数据库加载数据到内存)
userCache = new ArrayList<>();
userCache.add("admin");
userCache.add("test");
userCache.add("guest");
System.out.println("@PostConstruct:UserDao 缓存初始化完成,缓存数量:" + userCache.size());
// 实战:启动定时任务(模拟定时刷新缓存)
// ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
// executor.scheduleAtFixedRate(this::refreshCache, 0, 10, TimeUnit.MINUTES);
}
// 模拟刷新缓存方法
public void refreshCache() {
userCache.clear();
userCache.add("admin");
userCache.add("test");
System.out.println("@PostConstruct:缓存刷新完成");
}
// 模拟数据源(用于依赖注入)
@Component
public static class DataSource {
private String url = "jdbc:mysql://localhost:3306/test";
private String username = "root";
private String password = "123456";
@Override
public String toString() {
return "DataSource{" + "url='" + url + ''' + ", username='" + username + ''' + '}';
}
}
}
触发执行方式:无需手动触发,只要在方法上添加 @PostConstruct 注解,且该 Bean 由 Spring 管理(如添加 @Component),Spring 会在 Bean 依赖注入完成后,自动调用该方法。
补充说明:与 InitializingBean 相比,@PostConstruct 无需实现接口,更简洁、更灵活,是日常开发的首选。
10. InitializingBean(Spring 原生初始化接口)
核心定位:Spring 原生的 Bean 初始化接口,功能与 @PostConstruct 几乎一致,常用于框架底层。
执行时机:@PostConstruct 方法执行后 → 自定义 init-method 执行前。
能拿到什么:当前 Bean 的所有注入属性、容器资源(如果实现了 Aware 接口),与 @PostConstruct 一致。
实战场景:框架底层初始化(如 Spring 内置组件 DataSourceTransactionManager),日常开发推荐用@PostConstruct,但需要了解该接口(面试常问)。
执行顺序:@PostConstruct → afterPropertiesSet()(InitializingBean 方法) → 自定义 init-method。
实战代码(可直接运行) :
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* 实战用途:Spring 原生初始化方式,常用于框架底层,演示执行顺序
*/
@Component
public class OrderService implements InitializingBean {
@Autowired
private UserDao userDao;
// 1. @PostConstruct 方法
@PostConstruct
public void postConstructInit() {
System.out.println("InitializingBean:1. @PostConstruct 执行:OrderService 初始化");
}
// 2. InitializingBean 方法(Spring 原生)
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean:2. afterPropertiesSet 执行:依赖注入的 UserDao:" + userDao);
// 实战:初始化业务逻辑(与 @PostConstruct 功能一致)
System.out.println("InitializingBean:OrderService 业务初始化完成");
}
// 3. 自定义 init-method(XML 或 @Bean 中配置)
public void customInitMethod() {
System.out.println("InitializingBean:3. 自定义 init-method 执行");
}
// 注册方式(如果不用 @Component,用 @Bean 配置 init-method)
// @Bean(initMethod = "customInitMethod")
// public OrderService orderService() {
// return new OrderService();
// }
}
触发执行方式:无需手动注册,只要实现 InitializingBean 接口,且该 Bean 由 Spring 管理(如添加 @Component),Spring 在 Bean 依赖注入完成后,会自动调用 afterPropertiesSet() 方法。
11. BeanPostProcessor(AOP、代理、增强核心)
核心定位:Bean 初始化前后的增强器,Spring 最核心的增强接口,AOP、事务、日志等功能的底层实现。
执行时机:@PostConstruct、InitializingBean 等初始化方法 之前 → 初始化方法 之后。
能拿到什么:Bean 实例、Bean 名称,能对 Bean 实例进行增强、替换(如创建代理对象)。
实战场景:AOP 代理、事务管理、日志记录、性能监控、权限校验,几乎所有 Spring 增强功能都依赖该接口。
实战代码(可直接运行) :
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* 实战用途:Bean 增强、AOP 代理、日志监控,模拟 Spring AOP 底层逻辑
*/
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
// 1. Bean 初始化方法(@PostConstruct、afterPropertiesSet)之前执行
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 只增强 Service 结尾的 Bean
if (beanName.endsWith("Service")) {
System.out.println("BeanPostProcessor:初始化前增强:" + beanName + ",Bean 实例:" + bean);
// 实战:日志记录(记录 Bean 初始化开始)
}
return bean; // 返回原 Bean,不修改
}
// 2. Bean 初始化方法之后执行,核心增强方法(可返回代理对象)
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 只增强 OrderService
if ("orderService".equals(beanName)) {
System.out.println("BeanPostProcessor:初始化后增强:" + beanName + ",开始创建代理对象");
// 实战:用 CGLIB 创建代理对象,实现 AOP 增强(日志、监控)
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(bean.getClass());
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
// 前置增强:日志记录、性能监控
long start = System.currentTimeMillis();
System.out.println("BeanPostProcessor:方法 " + method.getName() + " 开始执行");
// 执行原方法
Object result = proxy.invokeSuper(obj, args);
// 后置增强:记录执行时间
long end = System.currentTimeMillis();
System.out.println("BeanPostProcessor:方法 " + method.getName() + " 执行完成,耗时:" + (end - start) + "ms");
return result;
});
// 返回代理对象,替换原 Bean
return enhancer.create();
}
return bean; // 其他 Bean 返回原实例
}
}
触发执行方式:无需手动注册,给实现类添加 @Component 注解,Spring 自动扫描,在每个 Bean 初始化前后,都会自动调用该接口的两个方法。
补充说明:Spring AOP 的核心就是通过 BeanPostProcessor 在 Bean 初始化后创建代理对象,将切面逻辑织入到原方法中,上面的代码模拟了 AOP 的底层实现。
12. SmartInitializingSingleton(所有单例 Bean 就绪后)
核心定位:所有单例 Bean 创建完成后,容器即将就绪前的最后一步初始化,属于全局初始化扩展。
执行时机:所有单例 Bean 都实例化、初始化完成 → 容器就绪之前,是单例 Bean 初始化的最后一步。
能拿到什么:容器内所有单例 Bean(可通过 BeanFactory/ApplicationContext 获取),能执行全局初始化操作。
实战场景:全局缓存预热、框架启动后校验(如检查所有 Bean 是否符合规范)、事件广播(所有 Bean 就绪后发布)、依赖多个 Bean 的全局初始化。
实战代码(可直接运行) :
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
/**
* 实战用途:所有单例 Bean 就绪后,执行全局初始化、缓存预热
*/
@Component
public class CustomSmartInitializingSingleton implements SmartInitializingSingleton {
@Autowired
private ApplicationContext applicationContext;
@Autowired
private UserDao userDao;
@Override
public void afterSingletonsInstantiated() {
System.out.println("SmartInitializingSingleton:所有单例 Bean 已初始化完成,开始执行全局操作");
// 1. 拿到所有单例 Bean 名称,校验 Bean 规范(实战:检查所有 Service 都有 @Service 注解)
String[] singletonBeanNames = applicationContext.getBeanDefinitionNames();
int componentCount = 0;
for (String beanName : singletonBeanNames) {
if (applicationContext.getType(beanName) != null
&& applicationContext.getType(beanName).isAnnotationPresent(Component.class)) {
componentCount++;
}
}
System.out.println("SmartInitializingSingleton:容器中单例 Bean 数量:" + singletonBeanNames.length + ",Component 注解 Bean 数量:" + componentCount);
// 2. 全局缓存预热(实战:调用 UserDao 加载缓存,确保容器就绪后缓存可用)
userDao.refreshCache();
System.out.println("SmartInitializingSingleton:全局缓存预热完成");
// 3. 校验核心 Bean 是否存在(实战:确保数据源、事务管理器等核心组件已初始化)
boolean hasDataSource = applicationContext.containsBean("dataSource");
System.out.println("SmartInitializingSingleton:核心组件 dataSource 是否存在:" + hasDataSource);
}
}
触发执行方式:无需手动注册,给实现类添加 @Component 注解,Spring 会在所有单例 Bean 实例化、初始化完成后,自动调用 afterSingletonsInstantiated() 方法。
13. ContextRefreshedEvent(容器刷新完成事件)
核心定位:ApplicationContext 容器刷新完成(所有 Bean 初始化完成)后触发的事件,标志着容器正式就绪。
执行时机:SmartInitializingSingleton 执行完成 → ApplicationReadyEvent 执行前,容器已完成所有 Bean 的初始化和增强。
能拿到什么:ApplicationContext(上下文),可获取所有初始化完成的 Bean,执行容器就绪后的全局操作。
实战场景:容器刷新完成后的通知、全局组件初始化、第三方组件集成(如 Dubbo 服务注册)。
实战代码(可直接运行) :
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
/**
* 实战用途:容器刷新完成后,执行全局通知、第三方组件集成
*/
@Component
public class CustomContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 拿到核心资源:上下文(已刷新完成,所有 Bean 都已初始化)
ApplicationContext context = event.getApplicationContext();
System.out.println("ContextRefreshedEvent:容器刷新完成,上下文 ID:" + context.getId());
// 实战:验证所有核心 Bean 都已初始化
boolean hasOrderService = context.containsBean("orderService");
boolean hasUserDao = context.containsBean("userDao");
System.out.println("ContextRefreshedEvent:核心 Bean 验证:orderService=" + hasOrderService + ",userDao=" + hasUserDao);
// 实战:集成第三方组件(如 Dubbo 服务注册、MQ 消费者启动)
System.out.println("ContextRefreshedEvent:集成第三方组件,启动 Dubbo 服务注册...");
// 模拟 Dubbo 服务注册逻辑
// DubboRegistry registry = context.getBean(DubboRegistry.class);
// registry.registerService(OrderService.class);
// 实战:发布容器就绪通知(如通知监控系统,应用已启动完成)
System.out.println("ContextRefreshedEvent:Spring Boot 应用容器已完全就绪,可正常提供服务");
}
}
触发执行方式:无需手动注册,给实现类添加 @Component 注解,Spring 会自动扫描并监听该事件,容器刷新完成后自动触发。
补充说明:若存在父子容器(如 Spring MVC 与 Spring 容器),该事件会触发两次,可通过 event.getApplicationContext().getParent() == null 判断是否为根容器,避免重复执行逻辑。
14. ApplicationReadyEvent(应用就绪事件)
核心定位:整个 Spring Boot 应用完全就绪,可正常接收请求、处理业务的最终事件,是启动阶段的最后一个扩展点。
执行时机:ContextRefreshedEvent 执行完成 → 应用启动完成 → 可正常对外提供服务,晚于所有启动相关扩展点。
能拿到什么:ApplicationContext(上下文)、SpringApplication(应用实例)、ApplicationArguments(启动参数),能执行应用就绪后的最终操作。
实战场景:应用启动完成通知(如给运维发送启动成功消息)、启动参数校验、初始化业务数据(如初始化管理员账号)、启动成功日志打印。
实战代码(可直接运行) :
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
/**
* 实战用途:应用完全就绪后,执行启动通知、业务初始化、启动参数校验
*/
@Component
public class CustomApplicationReadyListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// 拿到核心资源:上下文、应用实例、启动参数
ApplicationContext context = event.getApplicationContext();
String appName = context.getEnvironment().getProperty("my.app.name");
String[] args = event.getArgs(); // 启动参数(如 --server.port=8080)
// 1. 打印启动成功日志(实战:标准化启动日志,方便运维排查)
System.out.println("===============================================");
System.out.println("ApplicationReadyEvent:【" + appName + "】应用启动完成!");
System.out.println("启动参数:" + String.join(" ", args));
System.out.println("应用就绪,可正常接收请求,端口:" + context.getEnvironment().getProperty("server.port", "8080"));
System.out.println("===============================================");
// 2. 实战:初始化业务数据(如创建默认管理员账号,避免手动操作)
initDefaultAdmin(context);
// 3. 实战:校验启动参数(确保必填参数已配置)
validateStartupArgs(context);
}
// 模拟:初始化默认管理员账号
private void initDefaultAdmin(ApplicationContext context) {
// UserDao userDao = context.getBean(UserDao.class);
// if (!userDao.exists("admin")) {
// userDao.createAdmin("admin", "123456");
// System.out.println("ApplicationReadyEvent:默认管理员账号初始化完成");
// }
System.out.println("ApplicationReadyEvent:默认管理员账号初始化完成(模拟)");
}
// 模拟:校验启动参数(如必须配置环境参数)
private void validateStartupArgs(ApplicationContext context) {
String activeProfile = context.getEnvironment().getActiveProfiles()[0];
if (!"dev".equals(activeProfile) && !"prod".equals(activeProfile)) {
throw new RuntimeException("启动参数异常:必须指定激活环境(dev/prod)");
}
System.out.println("ApplicationReadyEvent:启动参数校验通过,当前环境:" + activeProfile);
}
}
触发执行方式:无需手动注册,添加 @Component 注解,Spring 自动扫描,应用完全就绪后自动触发该事件。
关键区别:与 ContextRefreshedEvent 相比,ApplicationReadyEvent 标志着“应用可正常对外提供服务”,而 ContextRefreshedEvent 仅标志“容器刷新完成”,此时应用可能还未完全就绪(如第三方组件未集成完成)。
第4阶段:应用运行中(可选扩展)
这一阶段扩展点用于应用运行过程中的动态调整,日常开发中使用频率较低,但在框架开发、动态配置场景中非常重要。
15. ApplicationRunner(应用运行器)
核心定位:应用就绪后(与 ApplicationReadyEvent 时机一致),执行自定义运行逻辑,比事件监听更简洁,专注于“运行任务”。
执行时机:与 ApplicationReadyEvent 同时执行,应用完全就绪后,可执行长时间运行的任务(如定时任务启动、数据同步)。
能拿到什么:ApplicationArguments(启动参数),可获取启动时传入的参数,执行基于参数的业务逻辑。
实战场景:启动后执行数据同步、启动定时任务、基于启动参数执行自定义逻辑(如指定配置文件路径)。
实战代码(可直接运行) :
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
/**
* 实战用途:应用就绪后,执行数据同步、定时任务启动等运行逻辑
*/
@Component
public class CustomApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
// 拿到启动参数,判断是否需要执行数据同步
boolean needSync = args.containsOption("sync-data");
System.out.println("ApplicationRunner:启动参数校验,是否需要数据同步:" + needSync);
// 实战:启动数据同步任务(模拟从远程数据库同步数据到本地)
if (needSync) {
System.out.println("ApplicationRunner:开始执行数据同步任务...");
// 模拟数据同步逻辑
Thread.sleep(2000);
System.out.println("ApplicationRunner:数据同步完成,同步数据量:1000 条");
}
// 实战:启动定时任务(与 @PostConstruct 中启动定时任务一致,更专注于“运行阶段”)
System.out.println("ApplicationRunner:启动定时任务,每10分钟刷新一次缓存");
// ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
// executor.scheduleAtFixedRate(() -> System.out.println("定时刷新缓存"), 0, 10, TimeUnit.MINUTES);
}
}
16. CommandLineRunner(命令行运行器)
核心定位:与 ApplicationRunner 功能几乎一致,区别在于接收的启动参数格式不同,更适合命令行启动场景。
执行时机:与 ApplicationRunner 同时执行,晚于 ApplicationReadyEvent,应用完全就绪后执行。
能拿到什么:String[] args(原始命令行参数),不解析参数格式,直接获取所有启动参数。
实战场景:命令行启动时执行自定义逻辑、解析原始命令行参数、执行一次性启动任务。
实战代码(可直接运行) :
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
/**
* 实战用途:命令行启动场景,解析原始命令行参数,执行一次性启动任务
*/
@Component
public class CustomCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
// 拿到原始命令行参数(不解析,直接获取)
System.out.println("CommandLineRunner:原始命令行参数数量:" + args.length);
System.out.println("CommandLineRunner:所有命令行参数:" + String.join(" ", args));
// 实战:解析命令行参数(如 --env=prod 表示生产环境)
for (String arg : args) {
if (arg.startsWith("--env=")) {
String env = arg.split("=")[1];
System.out.println("CommandLineRunner:解析命令行参数,当前环境:" + env);
}
}
// 实战:执行一次性启动任务(如初始化日志配置)
System.out.println("CommandLineRunner:初始化日志配置,设置日志级别为 INFO");
}
}
触发执行方式:无需手动注册,添加 @Component 注解,Spring 自动扫描,应用就绪后自动执行 run 方法。
与 ApplicationRunner 区别:
- ApplicationRunner:接收 ApplicationArguments 类型参数,可解析 --key=value 格式参数(支持选项、非选项参数);
- CommandLineRunner:接收 String[] 类型参数,仅获取原始命令行参数,不解析格式,更灵活但需手动解析。
第5阶段:容器销毁(优雅停机,Spring Boot 2.3+ 支持)
这一阶段扩展点用于容器销毁前的资源清理,避免资源泄漏(如数据库连接、线程池、文件流),是生产环境必备的扩展点。
17. DisposableBean + @PreDestroy(Bean 销毁扩展)
核心定位:Bean 销毁前的资源清理扩展,是 Spring 原生的销毁方式,配合 @PreDestroy 注解,实现优雅停机。
执行时机:容器开始销毁 → Bean 销毁前执行,用于释放资源(如关闭数据库连接、停止线程池、关闭文件流)。
能拿到什么:当前 Bean 实例及注入的资源,可执行资源释放逻辑。
实战场景:关闭数据库连接池、停止定时任务、释放文件资源、取消第三方服务注册(如 Dubbo 服务注销),避免资源泄漏。
实战代码(可直接运行) :
import org.springframework.beans.factory.DisposableBean;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 实战用途:Bean 销毁前释放资源,实现优雅停机,避免资源泄漏
*/
@Component
public class CustomDisposableBean implements DisposableBean {
// 模拟:线程池资源(需在销毁时关闭)
private final ExecutorService executorService = Executors.newFixedThreadPool(5);
// 模拟:文件流资源(需在销毁时关闭)
// private FileInputStream fileInputStream;
// 1. @PreDestroy 注解方法(JSR-250 规范,与 @PostConstruct 对应)
@PreDestroy
public void preDestroy() {
System.out.println("@PreDestroy:Bean 销毁前执行,释放文件流资源");
// 模拟关闭文件流
// if (fileInputStream != null) {
// try {
// fileInputStream.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
}
// 2. DisposableBean 接口方法(Spring 原生销毁方法)
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean:Bean 销毁中,释放线程池资源");
// 关闭线程池(优雅关闭,等待所有任务执行完成)
executorService.shutdown();
while (!executorService.isTerminated()) {
Thread.sleep(100);
}
System.out.println("DisposableBean:线程池已优雅关闭,所有任务执行完成");
// 实战:注销第三方服务(如 Dubbo 服务注销)
System.out.println("DisposableBean:注销 Dubbo 服务,避免服务残留");
}
// 模拟:使用线程池执行任务
public void executeTask(Runnable task) {
executorService.submit(task);
}
}
执行顺序:@PreDestroy 方法 → destroy()(DisposableBean 方法) → 自定义 destroy-method。
触发执行方式:无需手动触发,只要 Bean 由 Spring 管理(如添加 @Component),且实现 DisposableBean 接口或添加 @PreDestroy 注解,容器销毁时会自动调用对应方法。
补充说明:Spring Boot 2.3+ 支持优雅停机(通过 server.shutdown=graceful 配置),此时容器会等待所有正在执行的任务完成后再销毁 Bean,配合 DisposableBean + @PreDestroy 可实现完美的资源释放。
三、17大扩展点执行顺序总结(面试高频)
结合前文所有扩展点,整理出真实执行顺序(不可逆,面试直接背),按阶段划分,一目了然:
- 第0阶段(环境准备) :EnvironmentPostProcessor → ApplicationEnvironmentPreparedEvent
- 第1阶段(容器初始化前) :ApplicationContextInitializer
- 第2阶段(容器初始化) :ApplicationPreparedEvent
- 第3阶段(容器刷新/Bean 相关) :BeanDefinitionRegistryPostProcessor → BeanFactoryPostProcessor → InstantiationAwareBeanPostProcessor(实例化前) → InstantiationAwareBeanPostProcessor(实例化后) → InstantiationAwareBeanPostProcessor(依赖注入前) → Aware 系列接口 → @PostConstruct → InitializingBean → BeanPostProcessor(初始化前) → BeanPostProcessor(初始化后) → SmartInitializingSingleton → ContextRefreshedEvent
- 第4阶段(应用运行中) :ApplicationReadyEvent → ApplicationRunner → CommandLineRunner
- 第5阶段(容器销毁) :@PreDestroy → DisposableBean
四、面试高频考点补充
结合真实面试场景,补充3个高频问题,帮你快速加分:
- 问题1:Spring Boot 启动最早期的扩展点是什么?作用是什么? 答:EnvironmentPostProcessor,是 Spring 官方推荐的环境定制扩展口,执行时机最早(容器创建前),核心作用是注入配置源(如 Nacos 远程配置)、修改环境配置,优先级可高于配置文件。
- 问题2:BeanPostProcessor 和 InstantiationAwareBeanPostProcessor 的区别是什么? 答:两者都是 Bean 增强扩展,但执行时机和作用不同: - InstantiationAwareBeanPostProcessor:执行在 Bean 实例化前/后、依赖注入前,专注于“实例化过程拦截”; - BeanPostProcessor:执行在 Bean 初始化前/后,专注于“初始化后的增强”(如 AOP 代理)。
- 问题3:@PostConstruct、InitializingBean、init-method 的执行顺序是什么?日常开发首选哪个? 答:执行顺序:@PostConstruct → InitializingBean(afterPropertiesSet) → init-method;日常开发首选 @PostConstruct,无需实现接口,更简洁、灵活,符合 JSR-250 规范。
五、总结
本文整理的17大扩展点,覆盖 Spring Boot 从启动到销毁的全生命周期,每个扩展点都包含实战代码和真实场景,既能帮你看懂框架底层(如 Nacos、MyBatis),也能直接用于项目实战(如配置注入、AOP 增强、优雅停机)。
核心重点:记住执行顺序,理解每个扩展点“能拿到什么资源、能解决什么问题”,面试时既能答出顺序,也能结合场景说明用途,轻松应对中高级后端面试。
补充:所有代码均基于 Spring Boot 2.7.x,兼容 3.x 版本(仅注册方式有细微差异,文中已标注),复制到项目中,添加对应包名即可直接运行。