阿里面试:SpringBoot启动时, 如何执行扩展代码?
本文 的 原文 地址原始的内容,请参考 本文 的 原文 地址
本文 的 原文 地址
尼恩说在前面
在40岁老架构师 尼恩的读者交流群(50+)中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团、蚂蚁、得物的面试资格,遇到很多很重要的相关面试题:
阿里一面:如何在SpringBoot启动时执行特定代码?SpringBoot 的扩展点有哪些?SpringBoot 的扩展点、和SpringBoot 启动中的发布订阅的事件机制,有什么关系?SpringBoot 的扩展点 有哪些类型?你们项目中, 对 SpringBoot 进行过 哪些 扩展?最近有小伙伴在面 阿里,问到了相关的面试题,可以说是逢面必问。
小伙伴没有系统的去梳理和总结,所以支支吾吾的说了几句,面试官不满意,面试挂了。
所以,尼恩给大家做一下系统化、体系化的梳理,使得大家内力猛增,可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。
最终,机会爆表,实现”offer自由” 。
当然,这道面试题,以及参考答案,也会收入咱们的 《尼恩Java面试宝典PDF》V175版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。
《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请到文末公号【技术自由圈】获取本文作者:
[*]第一作者 老架构师 肖恩(肖恩 是尼恩团队 高级架构师,负责写此文的第一稿,初稿 )
[*]第二作者 老架构师 尼恩 (45岁老架构师, 负责 提升此文的 技术高度,让大家有一种 俯视 技术、俯瞰技术、 技术自由 的感觉)
现状:能 暴击面官的 顶级高手,不到10%
经过 尼恩团队 对 社群1000多 5年以上经验的开发小伙伴的了解和分析,真正能在 SpringBoot 的架构和源码这块掌握得好的人很少。
尽管大家都知道 SpringBoot 的架构和源码 非常重要,很多的人,一直在学这块,和研究这块,但是 太多的人 在死记硬背。
甚至说 90%的人, 在死记硬背,过两个月就忘记了。
面试的时候,也回答不到 Spring 的源码 底层思维/ 底层原理,
真正 理解了 Spring 的源码 底层思维/ 底层原理,做到 能 暴击面官的 ,比例 不到10%。
本文,尼恩从设计模式入手 , 带大家 穿透 Spring 的源码 , 帮大家 暴击面官。
问题答案
在 Spring Boot 应用中,主要通过 SpringBoot 的扩展点 ,实现 在启动阶段执行特定代码。
SpringBoot 主要的扩展点
方式 执行时机 特点 CommandLineRunner应用完全启动后,接收原始命令行参数简单直接,适合处理命令行参数ApplicationRunner应用完全启动后,接收结构化参数参数处理更灵活@EventListener监听特定启动事件可以精确控制执行阶段ApplicationListener实现接口监听应用事件更底层的事件控制SmartInitializingSingleton所有单例Bean初始化完成后适合需要在所有Bean就绪后执行的逻辑@PostConstructBean初始化完成后仅限于单个Bean的生命周期SpringApplicationRunListener贯穿整个启动过程需要实现多个方法,适合框架级扩展BeanPostProcessor每个Bean初始化前后粒度最细,可以干预每个Bean的创建过程2、扩展点的执行时机
spring boot 中这么多扩展点,源码中的触发点在那,在那个时机执行的?
如何选择合适的扩展点执行特定代码呢?
Spring Boot扩展点分类 与 选择
如果对于spring boot扩展点做大致分类,可以分为两类
[*]事件类 扩展点: 基于 观察者模式 的事件类扩展点 , 被动接收模式
[*]非事件类扩展点: 基于 模版模式 的非事件类扩展点(比如后置处理器 BeanFactoryPostProcessor,CommandLineRunner等) ,主动调用模式
1、扩展点分类统计
根据Spring Boot官方文档及源码分析,我将核心扩展点分类统计如下:
类型 扩展点名称/机制 数量 占比 事件类ApplicationEvent 子类12+75%〰️@EventListener 监听机制-非事件类ApplicationRunner125%〰️CommandLineRunner1〰️SpringApplicationRunListener1〰️ApplicationContextInitializer1〰️BeanFactoryPostProcessor1总计16+100%尼恩社群,一个塔尖、硬核 技术 研究圈 , 我们对 Spring Boot 扩展点分类统计 的研究结果是: 事件类扩展点 占绝对主导(≈75%),非事件类较少(≈25%)
(1) 事件类扩展点 占绝对主导(≈75%):
12个核心事件 + @EventListener机制
(2) 非事件类扩展点 较少(≈25%):
6个主要接口式扩展点 , 模版模式
(3) 未计入@PostConstruct等通用扩展机制
2、扩展点选择决策指南
(1)优先选择事件类扩展点的场景(推荐80%场景)
比如: 资源预热 → ApplicationReadyEvent
@EventListener(ApplicationReadyEvent.class) public void warmupCache() { // 异步预热缓存 CompletableFuture.runAsync(() -> cacheService.preload()); }
通过尼恩团队的研究, Spring Boot 12个核心启动事件详解
根据 Spring Boot 官方文档(3.x 版本)和源码分析,以下是构成事件类扩展点主体的 12 个核心事件及其触发时机和作用:
序号 事件类型 触发时机 核心作用 是否可修改容器 1ApplicationStartingEventSpringApplication.run() 执行后立即触发最早介入点,注册自定义监听器❌2BootstrapContextInitializedEvent引导上下文(BootstrapContext)初始化后配置加密/密钥管理的最佳时机❌3ApplicationEnvironmentPreparedEvent环境对象(Environment)准备完成,但尚未加载配置文件动态修改配置的最后机会(如:添加 PropertySource)✅4ApplicationContextInitializedEventApplicationContext 初始化完成,但尚未加载 Bean定义执行早期容器定制(如设置活动 profiles)✅5ApplicationPreparedEventBean 定义加载完成,但实例化之前最后修改 Bean 定义的机会(添加自定义 BeanPostProcessor)✅6ContextRefreshedEvent上下文完全刷新后触发(Spring Framework 原生事件)执行需要完整上下文的操作⚠️ 风险操作7WebServerInitializedEvent内嵌 Web 服务器(Tomcat/Jetty)启动完成获取服务器端口等运行时信息❌8ApplicationStartedEvent上下文已刷新,但未触发 ApplicationRunner资源预热准备(如缓存加载)⚠️ 只读访问9AvailabilityChangeEvent (Liveness)应用进入活动状态(LivenessState.CORRECT)Kubernetes 存活探针准备❌10ApplicationReadyEvent所有 ApplicationRunner 执行完毕业务初始化安全点(可安全访问服务)⚠️ 只读访问11AvailabilityChangeEvent (Readiness)应用就绪可接收流量(ReadinessState.ACCEPTING_TRAFFIC)Kubernetes 就绪探针触发❌12ApplicationFailedEvent启动过程任何阶段失败时触发失败处理(如发送告警、记录诊断日志)❌注:Spring Boot 2.x 中 ApplicationReadyEvent 和 ApplicationStartedEvent 合并为单一事件(2) 非事件类扩展点的选择 场景(20%特殊情况)
场景 推荐扩展点 优势 简单命令行参数处理CommandLineRunner参数直接访问应用级初始化逻辑ApplicationRunner封装好的 ApplicationArguments早期容器操作SpringApplicationRunListener在事件系统初始化前介入容器刷新前修改Bean定义BeanDefinitionRegistryPostProcessor深度控制Bean加载为啥 SpringApplicationRunListener 的属于非事件类扩展点?
SpringApplicationRunListener 是 Spring Boot 启动架构中的核心非事件类扩展点,尽管其名称包含 "Listener",但在扩展机制分类中属于非事件类扩展点。
1、分类依据
1). 实现机制对比
特征 事件类扩展点 SpringApplicationRunListener 继承关系实现 ApplicationListener独立接口,不继承任何事件接口触发方式被动接收事件广播主动调用(由 SpringApplication 直接触发)设计模式观察者模式模板方法模式注册方式通过 Spring Bean 或 SPI仅限 SPI(META-INF/spring.factories)2). 源码证明
在 Spring Boot 核心源码中:
public class SpringApplication { public ConfigurableApplicationContext run(String... args) { // 直接调用 RunListener 而非事件机制 listeners.starting(bootstrapContext, this.mainApplicationClass); listeners.environmentPrepared(...); // ... } }
这明确展示了主动调用的模板方法模式。
2、SpringApplicationRunListener 三重身份
1). 本质:非事件类扩展点
[*]实现的是程序化调用契约而非事件监听契约
[*]在 Spring 事件系统初始化之前执行(ApplicationStartingEvent 就是由它发出的)
2). 角色:事件机制的触发器
核心实现类 EventPublishingRunListener:
public class EventPublishingRunListener implements SpringApplicationRunListener { @Override public void starting(...) { // 它负责发布第一个事件 multicastEvent(new ApplicationStartingEvent(...)); } }
这是 Spring Boot 事件体系的引擎启动器。
3). 作用:完成 启动生命周期 全流程 的 编排
控制启动阶段流转:
sequenceDiagram SpringApplication->>RunListener: starting() RunListener->>EventSystem: 发布ApplicationStartingEvent SpringApplication->>RunListener: environmentPrepared() RunListener->>EventSystem: 发布EnvironmentPreparedEvent
3、与事件类扩展点的互动关系
[*]▶️ 紫色:SpringApplicationRunListener(非事件类)
[*]
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]