SpringBoot面试题01-ApplicationContextInitializer
核心面试题一览

一、ApplicationContextInitializer

1-1、ApplicationContextInitializer是什么?
ApplicationContextInitializer 是 Spring Boot 在启动的时候,在 ApplicationContext(Spring 容器)创建完但还没做 Bean 初始化之前,执行的一段“提前准备逻辑”。
通俗点说:
它让你有机会 在 Spring 容器真正开始工作之前,先插一句话,对容器做一些定制化处理。
1-2、为什么需要这个东西?
有时候我们想:
在所有 Bean 加载之前修改一些配置
动态添加属性,让项目根据外部条件自动变化
在 Bean 初始化前注册一些全局组件
做一些环境检查,比如判断当前是测试环境还是生产环境
但这个时候 Bean 还没创建好,我们不能直接用 @Bean @Autowired 那些东西。
所以就需要一种“比 Bean 更早执行”的东西——这就是 ApplicationContextInitializer。
1-3、它什么时候执行?
Spring Boot 启动顺序中:
启动 Spring Boot →
创建 ApplicationContext →
(此时 Bean 还没加载)
→ 执行 ApplicationContextInitializer
→ 开始加载 Bean → 项目启动成功
注意重点:它在 Bean 加载之前执行。
1-4、怎么用?(最简单的例子)

① 写一个类实现接口
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;public class MyAppInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext context) {System.out.println(">>> ApplicationContextInitializer: 容器创建好了,但 Bean 还没加载");// 比如给环境添加一个属性context.getEnvironment().getSystemProperties().put("myKey", "myValue");}
}
② 告诉 Spring Boot 使用它(注册)
在 resources/META-INF/spring.factories 文件中写:
org.springframework.context.ApplicationContextInitializer=\
com.example.demo.MyAppInitializer
(Spring Boot 就会自动在启动时加载它)
面试官会重点问:怎么注册? 你记住这个就行。
1-5、它能干啥?举两个常用场景
场景 1:根据环境动态改变配置
String profile = context.getEnvironment().getActiveProfiles()[0];
if ("prod".equals(profile)) {context.getEnvironment().getSystemProperties().put("logging.level.root", "ERROR");
}
效果:线上环境只打印 Error,减少日志压力。
场景 2:提前检查系统状态(如 Redis、数据库、配置文件)
if (!new File("/important/config.yml").exists()) {throw new RuntimeException("关键配置文件不存在,项目不允许启动!");
}
让项目安全而不是启动后报错。
1-6、面试官可能问的问题(附标准回答)
Q1:ApplicationContextInitializer 的作用是什么?
它用于在 Spring 容器刷新之前,对 ApplicationContext 进行提前定制,比如动态修改配置、注册组件、校验环境等。
Q2:它的执行时机是什么?
它在 ApplicationContext 创建好但 Bean 初始化之前执行,属于早期扩展点。
Q3:怎么注册 ApplicationContextInitializer?
在
META-INF/spring.factories中通过
org.springframework.context.ApplicationContextInitializer=你的类全限定名注册。
1-7、总结成一句话
ApplicationContextInitializer 是在 Spring 容器创建后但 Bean 加载前执行的扩展点,用来在项目启动初期对 ApplicationContext 做定制,比如修改环境配置或检查运行条件。
1-8、示例:给上下文环境对象context注入环境属性


【注意】:
spring.factories文件要自己创建:
示例:

代码讲解:
1、这段代码的作用是:
在 Spring 容器启动之前,往 Spring 的 Environment(环境配置)里手动添加一个自定义的属性来源。
具体来说,它添加了一条配置:
applicationName = big-event
这样之后,你在项目任何地方都可以通过:
@Value("${applicationName}")
private String appName;
或者:
environment.getProperty("applicationName")
来获取这个值。
2、为什么要这样做?
有时候,配置不是写死在配置文件里的,而是要根据运行环境,动态生成,比如:
读取外部系统信息
动态决定项目名称/版本
从数据库或远程配置中心加载参数
根据机房、区域、IP 动态决定配置
这时就可以利用 ApplicationContextInitializer 来提前往 Spring 环境里写配置。
3、按代码行解释
public void initialize(ConfigurableApplicationContext applicationContext) {
当 Spring 容器刚创建好,但 Bean 还没加载时,会调用这个方法。
Map<String,Object> myMap = new HashMap<>();
myMap.put("applicationName", "big-event");
准备了一组 你想注入到 Spring 配置环境中的键值对。
ConfigurableEnvironment environment = applicationContext.getEnvironment();
获取 环境对象,相当于读取 application.yml、system env、JVM 参数的地方。
MutablePropertySources propertySources = environment.getPropertySources();
获取 所有配置的来源 的管理器,比如:
系统环境变量
application.yml
application.properties
命令行参数
你手动添加的 Map
propertySources.addLast(new MapPropertySource("myMap", myMap));
把你创建的 Map 作为一个 新的配置来源 加进 Spring 配置系统。
addLast代表优先级最低,意思是:如果 application.yml 中也写了同名配置,则 yml 的会覆盖这个 Map。
4、简单总结一句话(面试官最爱听的)
这段代码利用
ApplicationContextInitializer在 Bean 初始化前,把一组 Map 类型的自定义配置注入到 Spring 的 Environment 中,使它能像 application.yml 一样被获取和使用。
5、面试官追问:为什么放到 Environment 里,而不是直接写配置文件?
你可以这样回答:
因为有些配置是动态生成的,不是固定写死的,必须在 Spring 容器启动前提前注入,而
ApplicationContextInitializer就是专门用来做这种启动早期运行逻辑的扩展点。
测试使用注入的自动以环境属性:

小结:

二、真实企业级场景
2-1、企业级真实场景:多环境动态加载租户(Tenant)配置
假设你现在做的是 SaaS 多租户系统(一个系统服务多个客户 / 企业)。
每个客户有自己:
数据库地址
Redis 前缀
系统名称
Logo、主题等配置
但是这些信息不能写死在 application.yml 里,因为每个客户都不同,而且可能会变化。
而且这些信息必须要在Spring 容器初始化前就准备好,否则后面 Bean(特别是 DataSource、RedisTemplate)初始化会失败。
这时我们就会这样做:
在 ApplicationContextInitializer 里:
读取租户 ID(可能来自域名、请求头、部署参数等)
根据租户 ID 查询数据库 / Nacos / Redis 中存储的租户配置
将这些配置注入到 Spring Environment
让 Spring 后面创建 Bean 时自动使用这些动态配置
1、示例代码
public class TenantConfigInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext context) {// 1. 根据运行参数获取租户 ID,例如部署时指定 -DtenantId=abcString tenantId = context.getEnvironment().getProperty("tenantId");// 2. 从数据库或配置中心加载租户配置(伪代码)Map<String, Object> tenantConfig = TenantConfigService.loadConfig(tenantId);// 3. 注入到 Spring 环境中ConfigurableEnvironment environment = context.getEnvironment();environment.getPropertySources().addFirst(new MapPropertySource("tenantConfig", tenantConfig));}
}
2、效果是什么?
之后在任何地方都可以这样写:
@Value("${datasource.url}")
private String dbUrl;
并且不同租户的数据源不需要改代码、不需要改配置文件,就能自动切换。
3、小结:
在企业里,我们一般会利用 ApplicationContextInitializer 来进行一些启动早期的动态配置操作。
比如在 SaaS 多租户系统中,我们需要根据租户 ID 动态加载不同的数据库连接、Redis 配置信息、系统名称等,而这些配置必须在 Spring 创建 Bean 之前准备好,所以我们会在 ApplicationContextInitializer 里从数据库或配置中心加载租户配置,并注入到 Environment 中,这样后续 Bean 初始化就会自动使用对应租户配置,实现真正的动态、多租户配置加载。
2-2、 2 个企业常用场景
| 场景 | 描述 | ApplicationContextInitializer 做啥 |
|---|---|---|
| 灰度发布 / 蓝绿部署 | 不同机器跑不同配置 | 启动前根据机器标签注入特定开关 |
| 多机房 / 多地区部署 | 不同 region 使用不同资源 | 启动时判断地区 → 动态注入资源配置 |
你现在应该这样理解它:
ApplicationContextInitializer = 在 Spring 容器真正开始工作前,执行一段提前注入或修改配置的逻辑。
而这东西最大用途就是:配置是动态的,不是在 yml 里写死的。
三、SaaS 系统扫盲
3-1、什么是 SaaS 系统?
SaaS 的全称是 Software as a Service,翻译叫:
软件即服务
什么意思?
以前软件是 买断 + 安装本地服务器 的,比如:
公司自己买服务器
自己部署系统
自己维护升级
这种叫 传统软件模式,又贵又麻烦。
而 SaaS 系统是把软件放在云端,用户只要:
打开浏览器
输账号密码
就能直接用。
无需安装、无需部署、无需维护。
SaaS 就像你用微信、钉钉、飞书、企业微信一样,点一下就能用。
3-2、一句话总结:
| 模式 | 描述 |
|---|---|
| 传统软件 | 软件买回去,自己安,自己养,自费修 |
| SaaS | 软件放云上,按月/按年订阅,厂商负责维护 |
3-3、为什么企业喜欢 SaaS?
因为:
✅ 不用买服务器
✅ 不用请专业运维
✅ 不用自己升级系统
✅ 不用担心安全补丁
✅ 成本低、上线快
你只要付 订阅费 就能用。
3-4、SaaS 和普通网站有什么区别?
普通网站:所有人用同一套系统
比如:
Bilibili
淘宝
抖音
大家都用同一个系统,数据也都存在一起。
SaaS 系统:一个系统服务很多不同公司,每家公司有自己的数据和配置
核心特点:
同一个系统 → 面向多个不同客户(租户) → 数据隔离
例如:
| 公司 | 系统里看到的内容 |
|---|---|
| A 公司 | A 的员工 / 客户 / 订单 |
| B 公司 | B 的员工 / 客户 / 订单 |
| C 公司 | C 的员工 / 客户 / 订单 |
同一个系统,不同公司看不见彼此。
3-5、举个SaaS 系统的例子
| 产品 | 是不是 SaaS? | 说明 |
|---|---|---|
| 企业微信 | ✅ 是 | 一个系统服务不同公司,登录后看到自己公司 |
| 钉钉 | ✅ 是 | 公司之间数据隔离 |
| 飞书 | ✅ 是 | 同理 |
| 12306 | ❌ 不是 | 所有人数据混在一起,不分租户公司 |
3-5、回到我们刚才说 ApplicationContextInitializer 的场景
SaaS 系统里,每个客户可能需要:
不同数据库
不同主题皮肤
不同系统名称
不同菜单功能开关
而这些东西必须在 Spring 容器还没初始化 Bean 前 注入,所以才需要:
ApplicationContextInitializer
来 提前把租户配置放到 Environment 中。
这样后续整个系统会“自动适配该租户”。

