Spring全家桶面试题, 只补充细节版本
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 - 获取SpringApplicationRunListenersSpringApplicationRunListeners 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 分钟能说清)
1. 启动入口
`@SpringBootApplication` =
`@EnableAutoConfiguration`(开自动装配)
+ `@ComponentScan`(扫自己写的组件)
+ `@SpringBootConfiguration`(声明配置类)。
2. 加载坐标
`EnableAutoConfiguration` 导入 `AutoConfigurationImportSelector`,它从
`META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports`
读取 140+ 个自动配置类的全限定名(Spring Boot 2.7+ 废弃 `spring.factories`,3.x 彻底移除此文件)。
3. 条件判断
每个自动配置类头上都挂着一堆 `@ConditionalOnXxx`:
- `@ConditionalOnClass` classpath 里有指定类才生效
- `@ConditionalOnMissingBean` 容器里没这 Bean 才帮你配
- `@ConditionalOnProperty` 配置项开关
- ……(还有 10 来种)
举例:`DataSourceAutoConfiguration` 只在
classpath 下有 `javax.sql.DataSource` 且用户没自己配 `DataSource` Bean 时才启动。
4. 参数绑定
生效后,配置类里用 `@EnableConfigurationProperties` 把
`application.yml` 中的 `spring.datasource.*` 绑定到 `DataSourceProperties`,
再调用 `DataSourceBuilder` 创建 `HikariDataSource` Bean 并注册。
5. 留给用户的“钩子”
- 完全替代:自己写一个 `@Bean`,自动配置就退让(`@ConditionalOnMissingBean`)。
- 微调参数:改 `application.yml` 即可。
- 关闭某段:
`spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration`
------------------------------------------------
二、源码速读路线(能点到文件名)
1. SpringApplication.run
↓
2. `refresh()` → `invokeBeanFactoryPostProcessors()`
↓
3. `ConfigurationClassPostProcessor` 解析 `@Configuration`
↓
4. `AutoConfigurationImportSelector.selectImports()`
读取 `META-INF/spring/*.imports` 文件,得到 `List<String> candidateConfigurations`
↓
5. `ConditionEvaluator` 按 `@ConditionalOnXxx` 过滤,生成 `MatchResult`
↓
6. 剩余配置类被 `ConfigurationClassParser` 加载,其内部 `@Bean` 方法被 `Cglib` 增强后注册到 `DefaultListableBeanFactory`
------------------------------------------------
三、实战自定义 starter(10 行代码能跑通)
需求:项目里只要引入 `my-sms-spring-boot-starter`,就自动注入一个 `SmsTemplate`。
1. 新建模块 `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());
}
}
```
2. 新建 `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports`
```
com.demo.sms.SmsAutoConfiguration
```
3. 再包一个 `my-sms-spring-boot-starter`(空 jar,只依赖上面 autoconfigure 模块 + 业务 SDK)
4. 业务方使用
```xml
<dependency>
<groupId>com.demo</groupId>
<artifactId>my-sms-spring-boot-starter</artifactId>
</dependency>
```
```yaml
sms.access-key=AKxxx
sms.secret=SECxxx
```
启动即得可注入的 `SmsTemplate`。
------------------------------------------------
四、常见坑 & 调优
1. 循环依赖报 `BeanCurrentlyInCreationException`
→ 检查自动配置顺序,用 `@AutoConfigureAfter/@AutoConfigureBefore` 调整。
2. 启动慢
→ 打开 `debug=true` 或 `spring.boot.autoconfigure.condition.OnClassCondition=TRACE` 看哪些条件未命中,排除无用配置。
3. 重复 Bean
→ 用 `spring.autoconfigure.exclude` 或在自定义 starter 里加 `@ConditionalOnMissingBean`。
------------------------------------------------
一句话收拢
自动装配 = **“在正确的时间,把正确的 Bean,按正确的配置,塞进容器”**;
而 SpringBoot 把这套流程固化成了**条件注解 + 配置类 + META-INF 索引**,
既让**用户零配置**,又留足**扩展/覆盖/排错**的口子,这才是它“开箱即用”的本质。
面试口诀(3 句背完)
1. 入口一注解:
`@SpringBootApplication` 里藏 `@EnableAutoConfiguration`,开启自动装配。
2. 加载两步走:
① 读文件:`META-INF/spring/*.imports` 拿出 140+ 自动配置类。
② 做判断:`@ConditionalOnClass/@OnMissingBean` 等条件过滤,只留需要的。
3. 结果零配置:
条件成立就帮你注册 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
