Spring全家桶面试题, 补充细节版本(2021优化版)
@$Spring Boot的启动流程,分为以下两大部分:
SpringApplication的实例化
-
推断应用类型是否是Web环境
-
设置初始化器(Initializer)
-
设置监听器(Listener)
-
推断应用入口类(Main)
SpringApplication.run方法
-
获取SpringApplicationRunListeners
-
准备配置环境ConfigurableEnvironment
-
创建ApplicationContext应用上下文
-
ApplicationContext前置处理
-
ApplicationContext刷新
-
ApplicationContext后置处理
完成了实例化,下面开始调用run方法
// 运行run方法
public ConfigurableApplicationContext run(String... args) {
// 此类通常用于监控开发过程中的性能,而不是生产应用程序的一部分。
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 设置java.awt.headless系统属性,默认为true
// Headless模式是系统的一种配置模式。在该模式下,系统缺少了显示设备、键盘或鼠标。
configureHeadlessProperty();
// KEY 1 - 获取SpringApplicationRunListeners
SpringApplicationRunListeners listeners = getRunListeners(args);
// 通知监听者,开始启动
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// KEY 2 - 根据SpringApplicationRunListeners以及参数来准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
// 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
Banner printedBanner = printBanner(environment);
// KEY 3 - 创建Spring上下文
context = createApplicationContext();
// 注册异常分析器
analyzers = new FailureAnalyzers(context);
// KEY 4 - Spring上下文前置处理
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// KEY 5 - Spring上下文刷新
refreshContext(context);
// KEY 6 - Spring上下文后置处理
afterRefresh(context, applicationArguments);
// 发出结束执行的事件
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
}
SpringBoot 自动装配
启动时扫描:SpringBoot 启动时,@EnableAutoConfiguration 触发对 `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports` 的扫描,获取所有候选自动配置类。
条件过滤:通过条件注解(如 @Conditional)筛选出符合当前环境的自动配置类。
注册 Bean:自动配置类中的 @Bean 方法向容器注册组件,完成自动装配。
SpringBoot 的"自动装配"**不是黑魔法**,而是一条**"约定优于配置"的装配生产线**:
`spring-boot-autoconfigure` 模块里预置了上百个`@Configuration`类,它们会**根据你引入的 jar、配置的参数以及运行环境**,**条件化地**把 Bean 注入容器。
一句话:**"你加依赖,我配 Bean;你写参数,我改行为;什么都不要,我就按默认值跑。"**
下面按"面试可讲、源码可看、实战可用"三层展开。
一、面试口语版(3 分钟能说清)
- 启动入口
`@SpringBootApplication` =
`@EnableAutoConfiguration`(开自动装配)
-
`@ComponentScan`(扫自己写的组件)
-
`@SpringBootConfiguration`(声明配置类)。
- 加载坐标
`EnableAutoConfiguration` 导入 `AutoConfigurationImportSelector`,它从
`META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports`
读取 140+ 个自动配置类的全限定名(Spring Boot 2.7+ 废弃 `spring.factories`,3.x 彻底移除此文件)。
- 条件判断
每个自动配置类头上都挂着一堆 `@ConditionalOnXxx`:
-
`@ConditionalOnClass` classpath 里有指定类才生效
-
`@ConditionalOnMissingBean` 容器里没这 Bean 才帮你配
-
`@ConditionalOnProperty` 配置项开关
-
......(还有 10 来种)
举例:`DataSourceAutoConfiguration` 只在
classpath 下有 `javax.sql.DataSource` 且用户没自己配 `DataSource` Bean 时才启动。
- 参数绑定
生效后,配置类里用 `@EnableConfigurationProperties` 把
`application.yml` 中的 `spring.datasource.*` 绑定到 `DataSourceProperties`,
再调用 `DataSourceBuilder` 创建 `HikariDataSource` Bean 并注册。
- 留给用户的"钩子"
-
完全替代:自己写一个 `@Bean`,自动配置就退让(`@ConditionalOnMissingBean`)。
-
微调参数:改 `application.yml` 即可。
-
关闭某段:
`spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration`
二、源码速读路线(能点到文件名)
- SpringApplication.run
↓
- `refresh()` → `invokeBeanFactoryPostProcessors()`
↓
- `ConfigurationClassPostProcessor` 解析 `@Configuration`
↓
- `AutoConfigurationImportSelector.selectImports()`
读取 `META-INF/spring/*.imports` 文件,得到 `List<String> candidateConfigurations`
↓
- `ConditionEvaluator` 按 `@ConditionalOnXxx` 过滤,生成 `MatchResult`
↓
- 剩余配置类被 `ConfigurationClassParser` 加载,其内部 `@Bean` 方法被 `Cglib` 增强后注册到 `DefaultListableBeanFactory`
三、实战自定义 starter(10 行代码能跑通)
需求:项目里只要引入 `my-sms-spring-boot-starter`,就自动注入一个 `SmsTemplate`。
- 新建模块 `my-sms-spring-boot-autoconfigure`
```java
@Configuration
@ConditionalOnClass(SmsTemplate.class)
@EnableConfigurationProperties(SmsProperties.class)
public class SmsAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SmsTemplate smsTemplate(SmsProperties props) {
return new SmsTemplate(props.getAccessKey(), props.getSecret());
}
}
```
- 新建 `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports`
```
com.demo.sms.SmsAutoConfiguration
```
-
再包一个 `my-sms-spring-boot-starter`(空 jar,只依赖上面 autoconfigure 模块 + 业务 SDK)
-
业务方使用
```xml
<dependency>
<groupId>com.demo</groupId>
<artifactId>my-sms-spring-boot-starter</artifactId>
</dependency>
```
```yaml
sms.access-key=AKxxx
sms.secret=SECxxx
```
启动即得可注入的 `SmsTemplate`。
四、常见坑 & 调优
- 循环依赖报 `BeanCurrentlyInCreationException`
→ 检查自动配置顺序,用 `@AutoConfigureAfter/@AutoConfigureBefore` 调整。
- 启动慢
→ 打开 `debug=true` 或 `spring.boot.autoconfigure.condition.OnClassCondition=TRACE` 看哪些条件未命中,排除无用配置。
- 重复 Bean
→ 用 `spring.autoconfigure.exclude` 或在自定义 starter 里加 `@ConditionalOnMissingBean`。
一句话收拢
自动装配 = **"在正确的时间,把正确的 Bean,按正确的配置,塞进容器"**;
而 SpringBoot 把这套流程固化成了**条件注解 + 配置类 + META-INF 索引**,
既让**用户零配置**,又留足**扩展/覆盖/排错**的口子,这才是它"开箱即用"的本质。
面试口诀(3 句背完)
- 入口一注解:
`@SpringBootApplication` 里藏 `@EnableAutoConfiguration`,开启自动装配。
- 加载两步走:
① 读文件:`META-INF/spring/*.imports` 拿出 140+ 自动配置类。
② 做判断:`@ConditionalOnClass/@OnMissingBean` 等条件过滤,只留需要的。
- 结果零配置:
条件成立就帮你注册 Bean,参数用 `@ConfigurationProperties` 绑定到 yml;
你写自己的 Bean 或改 yml,就能覆盖默认,实现"开箱即用"。
SPI 配置文件
SPI 配置文件变更 SpringBoot 3 中,自动配置类的注册文件从 META-INF/spring.factories 迁移到了 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (文本文件,每行一个自动配置类全类名)。原因:spring.factories 是 Spring 传统的 SPI 格式,而新格式更简洁,且避免了与其他 SPI 配置的冲突。示例(AutoConfiguration.imports):
com.example.config.MyAutoConfiguration
com.example.config.UserAutoConfiguration