本文专注讲解 Spring 容器全生命周期的扩展接口、执行顺序、核心作用、实战用法,重点补充每个接口的可运行代码,清晰演示"能拿到什么资源、怎么用",贴合真实开发场景,从入门到精通,一篇就够!
前言
在 Spring 开发中,我们很少直接手写底层扩展接口,但 Spring Boot 自动配置、MyBatis 整合、Nacos 配置中心、AOP、事务等核心功能,底层全靠 Spring 容器生命周期扩展接口实现。
作为开发者,掌握这些接口:
- ✅ 能看懂框架底层源码(比如 MyBatis 如何扫描 Mapper、Nacos 如何注入配置)
- ✅ 能实现自定义组件、动态配置(比如自定义 Starter、动态注册 Bean)
- ✅ 能解决启动加载、Bean 初始化、AOP 增强等实际问题
- ✅ 面试高频必问(Spring 生命周期+扩展点,几乎是中高级后端必考题)
本文整理 Spring 容器最核心的 10 大扩展接口 ,按执行先后顺序 排列,每个接口都包含:执行时机、核心作用、能拿到什么资源、实战代码示例、真实使用场景、触发执行方式,保姆级教程,新手也能直接复制运行,老手可直接用于实战。
一、Spring 容器生命周期总览(极简版)
先记住整体流程,后面逐个接口详解(每个接口对应流程中的一个节点):
启动引导 → 环境准备 → 上下文初始化 → 工厂后置处理 → Bean 定义加载 → Bean 实例化 → Bean 初始化 → AOP 增强 → 容器就绪 → 容器销毁
所有扩展接口严格按顺序执行,这是 Spring 最核心的规则,也是理解扩展点的关键。
二、10大核心扩展接口(按执行顺序,含实战代码+触发方式)
重点说明:所有代码均基于 Spring Boot 2.7.x(最常用稳定版本),可直接复制到项目中运行,每个代码示例都标注"能拿到什么""实战用途",贴合真实开发;同时补充触发执行方式,明确"实现接口后如何让 Spring 调用它"。
1. ApplicationContextInitializer(最早执行)
核心定位:容器刷新前的环境/上下文定制,Spring 最早的扩展点
执行时机 :在 ConfigurableApplicationContext 实例创建完成后,但在调用 context.refresh() 刷新容器之前,此时上下文还未初始化,Bean 定义还未加载。
能拿到什么:ConfigurableApplicationContext(可配置上下文)、Environment(环境对象),能操作环境配置、上下文参数
实战场景:启动时加载配置中心(如 Nacos)配置、激活指定环境(dev/prod)、添加自定义配置源
多重注册的处理: 如果通过多种方式注册了同一个 initializer,或者注册了多个 initializer,它们会按照注册的顺序依次执行。
- Spring Boot 内部会将通过
spring.factories、配置文件和 API 方式注册的 initializer 合并到一个列表中。 - 通常顺序是:
spring.factories(自动加载) -> 配置文件 (context.initializer.classes) -> API (addInitializers)。后注册的通常排在列表后面,但具体合并逻辑视 Spring Boot 版本实现而定,建议避免重复注册。
实战代码(可直接运行) :
java
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. 拿到环境对象(核心资源),激活 dev 环境
ConfigurableEnvironment environment = context.getEnvironment();
environment.setActiveProfiles("dev"); // 激活 dev 环境,对应 application-dev.yml
// 2. 拿到上下文,设置上下文ID(可用于日志、监控)
context.setId("my-spring-container");
System.out.println("上下文ID:" + context.getId());
// 3. 添加自定义配置源(模拟从配置中心拉取的配置)
Map<String, Object> configMap = new HashMap<>();
configMap.put("app.name", "spring-lifecycle-demo");
configMap.put("app.version", "1.0.0");
// 将自定义配置源添加到最前面,优先级最高(覆盖配置文件中的同名配置)
environment.getPropertySources().addFirst(new MapPropertySource("customConfig", configMap));
// 4. 验证配置是否生效(拿到配置值)
String appName = environment.getProperty("app.name");
System.out.println("从自定义配置源拿到app.name:" + appName);
}
}
代码说明:通过该接口,能在容器启动最早期拿到环境和上下文,提前配置环境、注入配置,这是 Nacos、Apollo 等配置中心的核心实现方式之一。
触发执行方式:需要手动注册,共4种方式,2种常用方式(二选一即可):
1. 通过 spring.factories 文件自动加载(最常用/推荐)
这是开发 Spring Boot Starter 或希望 initializer 对所有应用自动生效时的标准方式。Spring Boot 启动时会自动扫描类路径下的配置文件并实例化其中定义的 initializer。
-
配置位置 :
src/main/resources/META-INF/spring.factories- 注意:在 Spring Boot 3.x 及 Spring Framework 6+ 中,推荐使用新的文件路径
src/main/resources/META-INF/spring/org.springframework.context.ApplicationContextInitializer.imports,内容为全限定类名列表。旧版的spring.factories依然兼容但逐渐被弃用。
- 注意:在 Spring Boot 3.x 及 Spring Framework 6+ 中,推荐使用新的文件路径
-
配置内容 (spring.factories) :
propertiesorg.springframework.context.ApplicationContextInitializer=\ com.example.CustomApplicationContextInitializer -
配置内容 (Spring Boot 3+ .imports) :
textcom.example.CustomApplicationContextInitializer -
触发时机 :
SpringApplication构造过程中,通过SpringFactoriesLoader自动加载。
2. 通过 application.properties / application.yml 配置
如果你不想修改 META-INF 文件,或者只想在特定应用中启用,可以在配置文件中指定。
-
配置项 :
context.initializer.classes -
配置示例 (application.properties) :
propertiescontext.initializer.classes=com.example.CustomApplicationContextInitializer,com.example.AnotherInitializer -
配置示例 (application.yml) :
yamlspring: main: web-application-type: servlet # 其他配置... context: initializer: classes: com.example.CustomApplicationContextInitializer(注:在某些版本中直接写
context.initializer.classes即可,具体取决于 Spring Boot 版本对配置绑定的支持)
3. 通过 SpringApplication API 编程式注册
在 main 方法中,手动创建 SpringApplication 对象并调用 addInitializers 方法。这种方式优先级高,且完全由代码控制。
-
代码示例 :
javapublic static void main(String[] args) { SpringApplication app = new SpringApplication(MyApplication.class); // 添加自定义 initializer app.addInitializers(new CustomApplicationContextInitializer()); // 也可以添加多个 // app.addInitializers(new AnotherInitializer()); app.run(args); }
4. 通过 SpringApplicationBuilder 链式调用
如果你使用 SpringApplicationBuilder 来构建应用(常用于测试或更复杂的构建场景),可以使用 .initializers() 方法。
-
代码示例 :
javapublic static void main(String[] args) { new SpringApplicationBuilder(MyApplication.class) .initializers(new MyCustomInitializer()) .run(args); }
2. BeanDefinitionRegistryPostProcessor
核心定位:Bean 定义注册阶段,动态注册/修改 Bean 定义(无实例化)
执行时机 :加载 Bean 定义阶段,早于BeanFactoryPostProcessor,此时所有默认 Bean 定义已加载,但未实例化
能拿到什么:BeanDefinitionRegistry(Bean 定义注册器)、ConfigurableListableBeanFactory(Bean 工厂),能操作 Bean 定义(注册、修改、删除)
实战场景:动态扫描包注册 Bean(如 MyBatis Mapper 扫描)、替换框架默认 Bean、条件注册 Bean
实战代码(可直接运行) :
java
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("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("动态注册 customUserService 成功");
}
// 父接口方法,可用于修改 Bean 工厂配置(可选实现)
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 拿到 Bean 工厂,可修改 Bean 定义的属性(如懒加载)
beanFactory.getBeanDefinition("customUserService").setLazyInit(false);
System.out.println("修改 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);
}
}
}
代码说明 :MyBatis 的@MapperScan 底层就是通过该接口实现的------扫描指定包下的接口,动态注册为 Bean 定义,无需手动写 @Bean。
触发执行方式 :无需手动注册,只要给实现类添加 @Component 注解(或其他 Spring 组件注解,如 @Service),让 Spring 扫描到,就会自动执行。
3. BeanFactoryPostProcessor
核心定位:Bean 工厂后置处理,配置级修改,无实例化
执行时机 :所有 Bean 定义已加载,但所有 Bean 都还没创建(实例化前)
能拿到什么:ConfigurableListableBeanFactory(Bean 工厂),能获取所有 Bean 定义、修改 Bean 定义的属性
实战场景 :替换配置文件占位符 ${}、修改 Bean 的作用域/懒加载、调整 Bean 属性值、统一配置 Bean 前缀
注意 :不能在这里实例化 Bean (比如调用 beanFactory.getBean()),会导致 Bean 提前初始化,破坏生命周期顺序。
实战代码(可直接运行) :
java
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("当前容器中 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("修改 " + beanName + " 懒加载为 false");
}
}
// 3. 模拟替换占位符(实战:动态替换配置文件中的 ${app.version})
// 这里简化实现,实际底层是 PropertyPlaceholderConfigurer(也是 BeanFactoryPostProcessor)
BeanDefinition userServiceDef = beanFactory.getBeanDefinition("customUserService");
String username = (String) userServiceDef.getPropertyValues().getPropertyValue("username").getValue();
// 替换占位符(模拟从配置文件读取)
userServiceDef.getPropertyValues().addPropertyValue("username", username.replace("admin", "root"));
System.out.println("替换 customUserService 的 username 为 root");
}
}
代码说明 :Spring 内置的 PropertyPlaceholderConfigurer(处理 ${} 占位符)、ConfigurationClassPostProcessor(处理@Configuration),本质都是 BeanFactoryPostProcessor。
触发执行方式 :与 BeanDefinitionRegistryPostProcessor 一致,无需手动注册,给实现类添加 @Component 注解,Spring 自动扫描并执行。
4. InstantiationAwareBeanPostProcessor
核心定位:Bean 实例化前/后、依赖注入前的最底层拦截器,Spring 最强大的 Bean 扩展接口
执行时机 :Bean 实例化之前 → 实例化之后 → 依赖注入之前
能拿到什么:Bean 类型、Bean 名称、Bean 实例(实例化后)、BeanDefinition,能控制实例化过程、替换 Bean 实例
实战场景:自定义 Bean 实例化(如替代默认构造方法)、实现单例/多例外的作用域(如请求域)、跳过 Bean 实例化、AOP 底层依赖(获取 Bean 实例进行代理)
实战代码(可直接运行) :
java
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("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("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("依赖注入前:修改 " + beanName + " 的属性");
// 可修改注入的属性(比如动态设置属性值)
// pvs = new MutablePropertyValues(pvs).addPropertyValue("username", "test");
}
return pvs;
}
}
代码说明 :Spring AOP 的 AnnotationAwareAspectJAutoProxyCreator 底层就是继承该接口,在实例化后、依赖注入前对 Bean 进行代理,这是 AOP 实现的核心步骤。
触发执行方式 :无需手动注册,给实现类添加 @Component注解,Spring 自动扫描,在 Bean 实例化阶段自动回调该接口的方法。
5. Aware 系列接口(BeanNameAware / BeanFactoryAware / ApplicationContextAware)
核心定位:让 Bean 感知容器自身信息,获得容器的核心能力
执行时机 :Bean 实例化后,填充属性(依赖注入)完成,初始化方法(@PostConstruct)之前
能拿到什么:
- BeanNameAware:拿到当前 Bean 的名称(String 类型)
- BeanFactoryAware:拿到 BeanFactory(Bean 工厂),可手动获取容器内的 Bean
- ApplicationContextAware:拿到 ApplicationContext(上下文),拥有比 BeanFactory 更丰富的功能(如事件发布、资源加载)
实战场景:在自定义 Bean 中手动获取其他 Bean、加载资源文件、发布事件,是日常开发中最常用的扩展方式之一。
实战代码(可直接运行) :
java
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("从 BeanNameAware 拿到 Bean 名称:" + beanName);
}
// BeanFactoryAware 方法:设置 Bean 工厂
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
System.out.println("从 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("从 ApplicationContextAware 拿到上下文:" + applicationContext);
// 实战:获取环境配置(上下文比 BeanFactory 功能更全)
String appName = applicationContext.getEnvironment().getProperty("app.name");
System.out.println("从上下文拿到 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("收到事件:" + event.getMessage());
}
}
}
代码说明 :日常开发中,如果你需要在一个 Bean 中获取另一个 Bean,又不想用 @Autowired(比如动态获取),就可以通过 BeanFactoryAware 或 ApplicationContextAware 实现,灵活且可控。
触发执行方式 :无需任何手动注册,只要实现该系列接口的 Bean 是由 Spring 容器管理的(如添加 @Component、@Bean 注解),Spring 在创建该 Bean 时,会自动调用对应接口的方法(如 setBeanName、setApplicationContext)。
6. @PostConstruct(最常用的初始化方法)
核心定位:Bean 初始化阶段,最常用、最简洁的初始化方法(JSR-250 规范,非 Spring 原生,但 Spring 完全支持)
执行时机 :依赖注入完成后,InitializingBean 之前
能拿到什么:当前 Bean 的所有注入属性、容器资源(如果实现了 Aware 接口),能执行 Bean 的初始化业务逻辑
实战场景:加载初始化数据、初始化数据库连接、启动定时任务、初始化缓存,日常开发 80% 的初始化场景用它就够。
实战代码(可直接运行) :
java
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("UserDao 初始化:依赖注入的数据源:" + dataSource);
// 实战:初始化缓存(从数据库加载数据到内存)
userCache = new ArrayList<>();
userCache.add("admin");
userCache.add("test");
userCache.add("guest");
System.out.println("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("缓存刷新完成");
}
// 模拟数据源(用于依赖注入)
@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 的首选方式,比 InitializingBean 更灵活。
触发执行方式 :无需手动触发,只要在方法上添加 @PostConstruct 注解,且该 Bean 由 Spring 管理(如添加@Component),Spring 会在 Bean 依赖注入完成后,自动调用该方法。
7. InitializingBean(Spring 原生初始化接口)
核心定位 :Spring 原生的 Bean 初始化接口,功能与 @PostConstruct 几乎一致
执行时机 :@PostConstruct 之后,自定义 init-method 之前
能拿到什么 :当前 Bean 的所有注入属性、容器资源(如果实现了 Aware 接口),与 @PostConstruct 一致
实战场景 :框架底层初始化(如 Spring 内置组件),日常开发推荐用 @PostConstruct,但需要了解该接口(面试常问)。
执行顺序 :@PostConstruct → afterPropertiesSet()(InitializingBean 方法) → 自定义 init-method
实战代码(可直接运行) :
java
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("1. @PostConstruct 执行:OrderService 初始化");
}
// 2. InitializingBean 方法(Spring 原生)
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("2. InitializingBean.afterPropertiesSet 执行:依赖注入的 UserDao:" + userDao);
// 实战:初始化业务逻辑(与 @PostConstruct 功能一致)
System.out.println("OrderService 业务初始化完成");
}
// 3. 自定义 init-method(XML 或 @Bean 中配置)
public void customInitMethod() {
System.out.println("3. 自定义 init-method 执行");
}
// 注册方式(如果不用 @Component,用 @Bean 配置 init-method)
// @Bean(initMethod = "customInitMethod")
// public OrderService orderService() {
// return new OrderService();
// }
}
代码说明 :该接口的核心是 afterPropertiesSet() 方法,意为"属性设置完成后执行",Spring 底层很多组件(如 DataSourceTransactionManager)都实现了该接口进行初始化。
触发执行方式 :无需手动注册,只要实现InitializingBean 接口,且该 Bean 由 Spring 管理(如添加 @Component),Spring 在 Bean 依赖注入完成后,会自动调用 afterPropertiesSet() 方法。
8. SmartInitializingSingleton
核心定位:所有单例 Bean 创建完成后,容器即将就绪前的最后一步初始化
执行时机 :所有单例 Bean 都实例化、初始化完成,容器就绪之前,是单例 Bean 初始化的最后一步
能拿到什么:容器内所有单例 Bean(可通过 BeanFactory/ApplicationContext 获取),能执行全局初始化操作
实战场景:全局缓存预热、框架启动后校验(如检查所有 Bean 是否符合规范)、事件广播(所有 Bean 就绪后发布)
实战代码(可直接运行) :
java
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("所有单例 Bean 已初始化完成,开始执行全局操作");
// 1. 拿到所有单例 Bean 名称,校验 Bean 规范(实战:检查所有 Service 都有 @Service 注解)
String[] singletonBeanNames = applicationContext.getBeanDefinitionNames();
int serviceCount = 0;
for (String beanName : singletonBeanNames) {
if (applicationContext.getType(beanName) != null
&& applicationContext.getType(beanName).isAnnotationPresent(Component.class)) {
serviceCount++;
}
}
System.out.println("容器中单例 Bean 数量:" + singletonBeanNames.length + ",Component 注解 Bean 数量:" + serviceCount);
// 2. 全局缓存预热(实战:调用 UserDao 加载缓存,确保容器就绪后缓存可用)
userDao.refreshCache();
System.out.println("全局缓存预热完成");
// 3. 校验核心 Bean 是否存在(实战:确保数据源、事务管理器等核心组件已初始化)
boolean hasDataSource = applicationContext.containsBean("dataSource");
System.out.println("核心组件 dataSource 是否存在:" + hasDataSource);
}
}
代码说明:该接口的核心优势是"等待所有单例 Bean 就绪",比如你有一个组件需要依赖多个其他 Bean 初始化完成后才能执行,用该接口最合适,避免出现"依赖 Bean 未初始化"的问题。
触发执行方式 :无需手动注册,给实现类添加 @Component 注解,Spring 会在所有单例 Bean 实例化、初始化完成后,自动调用 afterSingletonsInstantiated() 方法。
9. BeanPostProcessor(AOP、代理、增强核心)
核心定位:Bean 初始化前后的增强器,Spring 最核心的增强接口,AOP、事务、日志等功能的底层实现
执行时机 :@PostConstruct、InitializingBean 等初始化方法之前 → 初始化方法之后
能拿到什么:Bean 实例、Bean 名称,能对 Bean 实例进行增强、替换(如创建代理对象)
实战场景:AOP 代理、事务管理、日志记录、性能监控、权限校验,几乎所有 Spring 增强功能都依赖该接口。
实战代码(可直接运行) :
java
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("初始化前增强:" + 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("初始化后增强:" + 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("方法 " + method.getName() + " 开始执行");
// 执行原方法
Object result = proxy.invokeSuper(obj, args);
// 后置增强:记录执行时间
long end = System.currentTimeMillis();
System.out.println("方法 " + method.getName() + " 执行完成,耗时:" + (end - start) + "ms");
return result;
});
// 返回代理对象,替换原 Bean
return enhancer.create();
}
return bean; // 其他 Bean 返回原实例
}
}
代码说明 :Spring AOP 的核心就是通过 BeanPostProcessor 在 Bean 初始化后创建代理对象,将切面逻辑织入到原方法中。上面的代码模拟了 AOP 的底层实现,通过 CGLIB 代理 OrderService,实现方法执行日志和耗时统计。
触发执行方式 :无需手动注册,给实现类添加 @Component 注解,Spring 自动扫描,在每个 Bean 初始化前后,都会自动调用该接口的两个方法。
10. DisposableBean / @PreDestroy(容器销毁阶段)
核心定位:容器销毁时,Bean 的资源释放接口,实现优雅停机
执行时机:应用关闭、容器销毁时(如 Tomcat 停止、Spring Boot 应用 shutdown)
能拿到什么:当前 Bean 实例、容器资源(如果实现了 Aware 接口),能执行资源释放逻辑
实战场景:关闭线程池、断开数据库连接、释放文件句柄、保存数据(如缓存写入数据库)、优雅停机清理
执行顺序 :@PreDestroy → destroy()(DisposableBean 方法) → 自定义 destroy-method
实战代码(可直接运行) :
java
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;
/**
* 实战用途:容器销毁时释放资源,实现优雅停机
*/
@Component
public class ResourceManager implements DisposableBean {
// 模拟线程池(需要在容器销毁时关闭)
private final ExecutorService executorService = Executors.newFixedThreadPool(5);
// 模拟数据库连接(需要在容器销毁时断开)
private final UserDao.DataSource dataSource = new UserDao.DataSource();
// 1. @PreDestroy 方法(日常开发首选)
@PreDestroy
public void preDestroyRelease() {
System.out.println("1. @PreDestroy 执行:开始释放资源");
// 关闭线程池(优雅关闭,等待所有任务执行完成)
executorService.shutdown();
System.out.println("线程池已关闭");
}
// 2. DisposableBean 方法(Spring 原生)
@Override
public void destroy() throws Exception {
System.out.println("2. DisposableBean.destroy 执行:释放剩余资源");
// 断开数据库连接(模拟)
System.out.println("数据库连接已断开:" + dataSource);
// 保存缓存数据到数据库(实战场景)
System.out.println("缓存数据已写入数据库,资源释放完成");
}
// 3. 自定义 destroy-method(可选)
public void customDestroyMethod() {
System.out.println("3. 自定义 destroy-method 执行:最终清理");
}
// 注册方式(如果不用 @Component,用 @Bean 配置 destroy-method)
// @Bean(destroyMethod = "customDestroyMethod")
// public ResourceManager resourceManager() {
// return new ResourceManager();
// }
}
代码说明:在生产环境中,优雅停机至关重要,该接口能确保容器关闭时,所有资源都能被正确释放,避免出现线程泄漏、数据库连接未关闭、数据丢失等问题。
触发执行方式 :无需手动触发,只要满足两个条件即可自动执行:1. 实现 DisposableBean 接口或给方法添加 @PreDestroy 注解;2. 该 Bean 由 Spring 管理;3. 应用正常关闭(如执行 shutdown 命令、停止 Tomcat 服务)。
补充:扩展接口触发判断速查表
如果你想快速判断一个扩展点怎么触发,记住这个简单表,开发时直接查,不用记:
| 扩展点 | 是否需要注册 | 触发方式 |
|---|---|---|
| ApplicationContextInitializer | 需要 | SpringApplication 手动 add 或 application.yml 配置 |
| BeanDefinitionRegistryPostProcessor | 不需要 | @Component 注解,Spring 自动扫描执行 |
| BeanFactoryPostProcessor | 不需要 | @Component 注解,Spring 自动扫描执行 |
| InstantiationAwareBeanPostProcessor | 不需要 | @Component 注解,Spring 自动扫描执行 |
| Aware 接口 | 不需要 | Bean 由 Spring 管理,自动回调接口方法 |
| @PostConstruct | 不需要 | Bean 由 Spring 管理,依赖注入后自动执行注解方法 |
| InitializingBean | 不需要 | Bean 由 Spring 管理,自动调用 afterPropertiesSet 方法 |
| SmartInitializingSingleton | 不需要 | @Component 注解,所有单例 Bean 就绪后自动执行 |
| BeanPostProcessor | 不需要 | @Component 注解,Spring 自动扫描,每个 Bean 初始化前后回调 |
| @PreDestroy / DisposableBean | 不需要 | Bean 由 Spring 管理,应用关闭、容器销毁时自动执行 |
核心规律:只要是 Spring 组件(加 @Component、@Bean、@Service 等),大部分扩展点都会自动触发;只有 ApplicationContextInitializer 需要手动注册,这是唯一例外。
三、全景执行顺序
java
1. ApplicationContextInitializer(容器刷新前,最早)
2. BeanDefinitionRegistryPostProcessor(Bean 定义注册,动态加 Bean)
3. BeanFactoryPostProcessor(Bean 工厂后置处理,改 Bean 定义)
4. InstantiationAwareBeanPostProcessor(Bean 实例化前/后、依赖注入前)
5. Aware 接口(BeanNameAware/BeanFactoryAware/ApplicationContextAware,感知容器)
6. @PostConstruct(Bean 初始化,最常用)
7. InitializingBean(Spring 原生初始化)
8. BeanPostProcessor(初始化前后,AOP 代理)
9. SmartInitializingSingleton(所有单例 Bean 就绪后)
10. @PreDestroy / DisposableBean(容器销毁,释放资源)
四、实战速查表(开发时直接查,不用记)
| 需求场景 | 推荐接口 | 能拿到什么核心资源 |
|---|---|---|
| 环境配置、激活 Profile、配置中心拉取 | ApplicationContextInitializer | ConfigurableApplicationContext、Environment |
| 动态注册 Bean、扫描包(如 MyBatis Mapper) | BeanDefinitionRegistryPostProcessor | BeanDefinitionRegistry、BeanFactory |
| 修改 Bean 配置、替换占位符 | BeanFactoryPostProcessor | ConfigurableListableBeanFactory |
| 控制 Bean 实例化、替换 Bean 实例 | InstantiationAwareBeanPostProcessor | Bean 类型、Bean 名称、Bean 实例 |
| 在 Bean 中获取容器、手动拿其他 Bean | ApplicationContextAware / BeanFactoryAware | ApplicationContext、BeanFactory |
| Bean 初始化、加载数据、启动定时任务 | @PostConstruct |