当前位置: 首页 > news >正文

Spring Boot 初始化钩子

Spring Boot 的"初始化钩子(Initialization Hooks)"指的是在 Spring Boot 应用启动阶段的各个生命周期点,可以插入自定义逻辑的机制。这些钩子让我们可以在 Spring 容器初始化前后、Bean 加载前后、应用启动完成后 等关键时机执行代码,实现各种定制化需求。


启动生命周期

启动生命周期概览

Spring Boot 启动过程大致可以分为几个阶段(对应可以挂钩的地方):

1️⃣ 创建并准备 SpringApplication 实例
2️⃣ 运行 ApplicationContext 初始化
3️⃣ 加载配置与环境 Environment
4️⃣ 创建 Bean 容器(ApplicationContext)
5️⃣ 注册 BeanDefinition
6️⃣ Bean 初始化(依赖注入、@PostConstruct)
7️⃣ 应用启动完成(ApplicationReadyEvent)
8️⃣ 进入运行状态

启动流程图

下面这张流程图清晰地展示了常见钩子的执行顺序及其在应用启动流程中的位置:

在这里插入图片描述


初始化钩子分类详解

🪝 1. 启动阶段前后钩子:SpringApplicationRunListener

如果你想在 Spring Boot 还没创建 ApplicationContext 前 就执行逻辑,比如打印启动参数、修改环境变量等:

public class MyRunListener implements SpringApplicationRunListener {public MyRunListener(SpringApplication application, String[] args) {}@Overridepublic void starting(ConfigurableBootstrapContext bootstrapContext) {System.out.println("🪝 应用启动中(还没创建Spring环境)");}@Overridepublic void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {System.out.println("🪝 环境已准备好,可以修改配置");}@Overridepublic void contextPrepared(ConfigurableApplicationContext context) {System.out.println("🪝 ApplicationContext 已创建但尚未加载bean");}@Overridepublic void contextLoaded(ConfigurableApplicationContext context) {System.out.println("🪝 BeanDefinition 已加载完毕");}@Overridepublic void started(ConfigurableApplicationContext context) {System.out.println("🪝 Spring 容器已启动完毕");}@Overridepublic void running(ConfigurableApplicationContext context) {System.out.println("🪝 应用已启动并在运行中");}@Overridepublic void failed(ConfigurableApplicationContext context, Throwable exception) {System.out.println("❌ 启动失败:" + exception.getMessage());}
}

注册方式(META-INF/spring.factories):

org.springframework.boot.SpringApplicationRunListener=\
com.example.MyRunListener

📍使用场景

  • 动态修改环境变量
  • 自定义日志系统初始化
  • 提前输出启动信息

🧩 2. Bean 初始化钩子:BeanPostProcessor

如果想在 每个 Bean 初始化前后 做事情(比如修改 Bean 属性、打印加载日志):

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {System.out.println("🌱 Bean 初始化前:" + beanName);return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {System.out.println("🌿 Bean 初始化后:" + beanName);return bean;}
}

📍使用场景

  • 动态代理 Bean(比如 AOP)
  • 自动注册拦截器、过滤器
  • 监控 Bean 加载性能

🔧 3. 应用启动后执行钩子:CommandLineRunner / ApplicationRunner

Spring Boot 在容器完全启动后,会自动执行这两个接口:

@Component
public class MyRunner implements CommandLineRunner {@Overridepublic void run(String... args) {System.out.println("🚀 应用启动完成,执行自定义逻辑");}
}

或:

@Component
public class MyAppRunner implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) {System.out.println("🚀 应用启动完成,参数:" + args.getOptionNames());}
}

📍使用场景

  • 初始化缓存
  • 检查配置、连接外部系统
  • 启动任务调度器、消息监听

🧠 4. Bean 自身生命周期钩子:@PostConstruct & InitializingBean

每个 Bean 自己可以通过这两种方式声明初始化逻辑:

@Component
public class MyService implements InitializingBean {@PostConstructpublic void initByAnnotation() {System.out.println("✨ @PostConstruct 执行");}@Overridepublic void afterPropertiesSet() {System.out.println("⚙️ afterPropertiesSet() 执行");}
}

执行顺序:

构造器 → 属性注入 → @PostConstruct → afterPropertiesSet()

📍使用场景

  • 初始化内部状态
  • 打开连接池
  • 校验配置参数

🧩 5. 容器事件监听钩子:ApplicationListener

可以监听 Spring 的事件,比如:

  • ApplicationStartingEvent
  • ApplicationReadyEvent
  • ContextRefreshedEvent
  • ContextClosedEvent

示例:

@Component
public class MyListener implements ApplicationListener<ApplicationReadyEvent> {@Overridepublic void onApplicationEvent(ApplicationReadyEvent event) {System.out.println("🟢 Spring Boot 启动完成!");}
}

📍使用场景

  • 延迟加载逻辑
  • 启动后执行异步任务
  • 应用关闭时清理资源

🌈 6. JVM 层面的钩子:Runtime.getRuntime().addShutdownHook

Spring Boot 会自动注册一个关闭钩子,但你也可以加自己的:

@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);Runtime.getRuntime().addShutdownHook(new Thread(() -> {System.out.println("🧹 JVM 即将关闭,清理资源...");}));}
}

📍使用场景

  • 清理临时文件
  • 优雅关闭线程池
  • 断开远程连接

核心钩子对比

钩子机制核心执行时机主要特点
@PostConstructBean 依赖注入完成后立即执行执行最早,适合单个 Bean 内部的简单初始化
SmartInitializingSingleton所有单例 Bean 初始化完成后执行适合执行依赖所有单例 Bean 的全局初始化逻辑
ApplicationRunner应用启动完成,即将开始服务前对命令行参数有良好的封装和解析能力
ApplicationReadyEvent应用完全就绪,可对外提供服务时是应用启动完成的最终信号,适合服务注册等

常见钩子执行顺序

需求场景推荐机制介入窗口粒度说明
修改上下文/提前注册单例ApplicationContextInitializer刷新前全局Bean 尚未创建,可操作 BeanFactory
观测全阶段、打点耗时SpringApplicationRunListener最早到最终全局可精细记录启动各段耗时
动态增/改 Bean 定义BeanDefinitionRegistryPostProcessor / BeanFactoryPostProcessor注册后、实例化前定义级改 scope / 属性 / 新增 Bean
包装增强 Bean 行为BeanPostProcessor初始化前后实例级典型用于代理/AOP/注入增强
单 Bean 内部初始化@PostConstruct / InitializingBean / initMethod依赖注入后单体轻量内部准备
全部单例就绪后一次性动作SmartInitializingSingleton所有单例创建后全局一次构建缓存/索引/映射
启动后执行脚本或预热CommandLineRunner / ApplicationRunner刷新已完成后置任务参数解析友好(ApplicationRunner)
应用真正对外服务就绪ApplicationReadyEvent端口开放后全局注册服务/健康检查触发点
生命周期可控组件SmartLifecycle启停阶段组件可按 phase 排序,与关闭协同
事件驱动扩展@EventListener / ApplicationListener各事件点解耦推荐使用 @EventListener 简化
优雅关闭清理资源ContextClosedEvent / ShutdownHook关闭时全局/组件释放连接、线程、临时文件

在这里插入图片描述


最佳实践

选择合适的钩子

  • 修改上下文结构/注册 Bean: ApplicationContextInitializer
  • 全局启动时序监听: SpringApplicationRunListener
  • 动态改 Bean 定义: BeanFactoryPostProcessor
  • 包装增强 Bean 行为: BeanPostProcessor
  • Bean 自身初始化: @PostConstruct / initMethod
  • 需要所有单例完成后: SmartInitializingSingleton
  • 启动后执行脚本/一次性任务: CommandLineRunner / ApplicationRunner
  • 生命周期受控组件: SmartLifecycle
  • 事件驱动扩展: ApplicationListener/@EventListener

典型应用场景

  • 多租户动态数据源注册: BeanDefinitionRegistryPostProcessor
  • 统一埋点启动阶段耗时: SpringApplicationRunListener
  • 自定义属性注入逻辑: BeanPostProcessor
  • 延迟预热缓存: ApplicationRunner
  • 启动后健康检查: ApplicationReadyEvent 监听
  • 优雅关闭资源: ContextClosedEvent 监听

优先级建议

  • 改上下文:Initializer > RunListener(若仅需注册单例)
  • 改 Bean 定义:RegistryPostProcessor(新增)优先于 FactoryPostProcessor(修改)
  • Bean 增强:首选 BeanPostProcessor,避免滥用 @PostConstruct 做增强
  • 启动任务:需要参数用 ApplicationRunner,否则 CommandLineRunner
  • 就绪信号:对外发布依赖 ApplicationReadyEvent,而非 Runner
  • 全局二次构建:SmartInitializingSingleton(避免在单 Bean 中串联依赖)

最佳实践

  1. 钩子职责单一,避免在一个接口里做多阶段逻辑。
  2. 控制执行顺序:当有多个初始化组件时,使用 @Order 注解明确指定执行顺序。
  3. 异常处理:在初始化逻辑中做好异常处理,避免启动失败。
  4. 避免长时间阻塞:启动阶段的初始化任务应尽快完成,耗时任务考虑异步执行。
  5. 环境差异化初始化:利用 @Profile 注解根据不同环境执行不同初始化逻辑。
  6. 注意关闭钩子:通过监听 ContextClosedEvent 实现资源的优雅关闭。

常见误区

  1. ApplicationContextInitializer 中访问业务 Bean:此时 Bean 尚未实例化。
  2. BeanPostProcessor 执行耗时长的全局逻辑:会影响所有 Bean 创建,拖慢启动。
  3. 混淆 CommandLineRunner 与就绪状态:Runner 执行不代表应用已对外就绪,若依赖暴露端口确认,需监听 ApplicationReadyEvent。
  4. 在 @PostConstruct 中启动线程阻塞:影响 Bean 初始化流程,应使用事件/Runner。

优先使用

  • 事件监听:@EventListener > 实现 ApplicationListener
  • 生命周期控制:SmartLifecycle > 手动在 Runner 中启动线程

误用速览:

误用替代建议
在 @PostConstruct 做复杂阻塞 IO延后到 ApplicationReadyEvent 或异步
使用 Runner 作为就绪判断改用 ApplicationReadyEvent
在 BeanPostProcessor 做重型扫描改用 SmartInitializingSingleton 或监听 RefreshedEvent
乱用 RunListener 修改业务 Bean此时 Bean 未创建,使用 FactoryPostProcessor

快速记忆

启动前:Initializer / RunListener
定义期:RegistryPostProcessor / FactoryPostProcessor
实例期:BeanPostProcessor + (@PostConstruct / afterPropertiesSet)
单例全局:SmartInitializingSingleton
刷新完成:ContextRefreshedEvent
启动后任务:Runner
对外就绪:ApplicationReadyEvent
可控生命周期:SmartLifecycle
事件驱动:@EventListener
优雅关闭:ContextClosedEvent / ShutdownHook

原口诀(保留):

Initializer 改上下文
RunListener 观测阶段
FactoryPostProcessor 改定义
PostProcessor 包装实例
@PostConstruct Bean 内自初始化
Runner 启动后任务
EventListener 订阅阶段事件
SmartLifecycle 生命周期组件

综合代码示例

@SpringBootApplication
public class DemoApp {public static void main(String[] args) {SpringApplication app = new SpringApplication(DemoApp.class);// 方式1:编程方式添加初始化器app.addInitializers(new MyCtxInitializer());app.run(args);}
}// 方式2:META-INF/spring.factories 注册
// org.springframework.context.ApplicationContextInitializer=\
// com.example.MyCtxInitializerclass MyCtxInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext ctx) {// 注册单例或修改上下文ctx.getBeanFactory().registerSingleton("startTimestamp", System.currentTimeMillis());}
}@Component
class MyBeanFactoryProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {// 修改 Bean 定义if (beanFactory.containsBeanDefinition("someService")) {BeanDefinition bd = beanFactory.getBeanDefinition("someService");bd.setScope("prototype"); // 修改作用域}}
}@Component
class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {// 例如:为特定 Bean 创建代理if (bean instanceof UserService) {// 对 UserService 进行包装}return bean;}
}@Component
class CacheWarmupRunner implements ApplicationRunner {@Autowiredprivate CacheService cacheService;@Overridepublic void run(ApplicationArguments args) {// 预热缓存cacheService.preloadData();}
}@Component
class ReadyStateListener {@EventListener(ApplicationReadyEvent.class)public void onReady() {// 应用已完全就绪,可对外服务System.out.println("应用已就绪,可以接受请求");}
}@Component
class GracefulShutdownListener implements ApplicationListener<ContextClosedEvent> {@Overridepublic void onApplicationEvent(ContextClosedEvent event) {System.out.println("应用正在关闭,进行清理...");// 关闭线程池、连接等}
}
http://www.dtcms.com/a/496226.html

相关文章:

  • 能打开所有网站的浏览器软件商店app下载安装
  • 斜纹水印全屏水印一键添加软件 批量处理 文字水印 图片水印 条纹水印
  • 【OpenHarmony】sensors_miscdevice小器件模块架构
  • 做物流网站有哪些内容网站 动态内容加速
  • Spring Boot 3零基础教程,WEB 开发 默认的自动配置,笔记25
  • 关键词推广软件哈尔滨网站优化页面
  • FREE下载:V2X方案之RSU介绍
  • 长春建站模板搭建网站用品推广网页
  • 推广网站哪家做的好网站是怎么盈利的
  • 台州免费自助建站模板怎么自己制作网站免费
  • Python处理淘宝API的JSONP与跨域问题
  • 多光谱图像颜色特征用于茶叶分类的研究进展
  • 做网站要学什么专业包装设计网站有哪些
  • 百度新网站收录做网站图片多少钱
  • 湖北网站推广可以做热图的工具网站
  • 实力网站优化公司首选哈尔滨模板建站系统
  • 辽宁市场网站建设销售做网站维护需要懂什么
  • 湘潭网站设计公司潍坊网站建设技术外包
  • 36.渗透-端口
  • 云南微网站搭建wp建站
  • 北京市城乡结合部建设领导小组办公室网站h5网页制作模板
  • 定制开发响应式网站做网站的工资高吗?
  • 六、MYSQL SQL语句
  • C语言入门(十):函数的深入认识
  • 签名机制 + JWT 鉴权 + Redis 防重放机制​​
  • 网站建设的局限性织梦网站文章内容模板
  • BugKu Web渗透 之 都过滤了
  • Qt模块间依赖与解耦问题
  • LoRA 微调大模型直观的理解
  • LeetCode 面试经典 150_栈_简化路径(53_71_C++_中等)(栈+stringstream)