第三部分:Spring Boot 与 Spring Cloud 高频选择题 40 道(含答案解析)
一、Spring Boot 启动、自动配置与配置文件(S01–S08)
S01 · 知识点:@SpringBootApplication 组合注解
【题目】按照 Spring Boot 官方定义,@SpringBootApplication 注解是由以下哪三个注解组合而成的? A. @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan B. @Configuration + @EnableAutoConfiguration + @ComponentScan C. @SpringBootConfiguration + @EnableCaching + @Import D. @Configuration + @EnableAutoConfiguration + @SpringBootTest
答案:A
【考点】@SpringBootApplication 组合注解的构成及其与 @Configuration 的关系。
【结论】选 A。@SpringBootApplication 由 @SpringBootConfiguration、@EnableAutoConfiguration 和 @ComponentScan 三个元注解组合而成。
【逐项辨析】
- A. 正确。 查看源码可知,@SpringBootApplication 上直接标注了这三个注解,属于官方精确声明的组合。
- B. 错误。 虽然 @SpringBootConfiguration 本质就是 @Configuration 的派生注解(元注解包含 @Configuration),功能层面两者等价,但严格按官方源码定义,组合成员是 @SpringBootConfiguration 而非 @Configuration。
- C. 错误。 @EnableCaching 用于开启缓存功能,@Import 用于导入配置类,二者均不属于 @SpringBootApplication 的组成部分。
- D. 错误。 @SpringBootTest 是测试模块的注解,与主类启动注解无关。
【知识点】 @SpringBootApplication 是一个便捷的“三合一大礼包”,其内部通过 @AliasFor 完成属性别名映射。
- @SpringBootConfiguration:继承自 @Configuration,标记该类为配置类,同时被 Spring Boot 专属工具链识别(如配置属性绑定时的特殊处理)。
- @EnableAutoConfiguration:通过 @Import(AutoConfigurationImportSelector.class) 触发自动配置。启动时读取 classpath 下 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(Spring Boot 2.7+)或 META-INF/spring.factories(旧版),加载候选自动配置类,再经 @Conditional 家族条件过滤,最终决定生效的 Bean。
- @ComponentScan:默认扫描当前启动类所在包及其子包下的 @Component、@Service、@Repository、@Controller 等 Spring 组件。可通过 basePackages 或 basePackageClasses 显式调整扫描路径。
值得注意的是,@SpringBootApplication 的 exclude 和 excludeName 属性实际是通过 @AliasFor 透传给 @EnableAutoConfiguration 的,用于排除特定自动配置类。
【记忆锚点】 “SpringBoot 靠 ComponentScan 找人,靠 EnableAuto 干活,自身是 Configuration。”——首字母 SCE(或记“三 C 一 E”中的核心 trio)。
【易混对比】
| 对比维度 | @Configuration | @SpringBootConfiguration |
|---|---|---|
| 来源 | Spring Framework 原生 | Spring Boot 专属 |
| 元注解关系 | 基础注解 | 元注解包含 @Configuration |
| 识别范围 | 所有 Spring 上下文 | Spring Boot 特定工具链额外识别 |
| 能否互换 | 功能层面可互换 | 官方组合声明中不可替代 |
| 对比维度 | @ComponentScan | @EnableAutoConfiguration |
|---|---|---|
| 职责 | 发现并注册用户自定义组件 | 加载第三方 starters 的预设配置 |
| 扫描目标 | 用户代码中的注解类 | classpath 下的 imports/factories 文件 |
| 可控方式 | basePackages / filter | exclude / @Conditional |
换问法: 若题目问“从功能等价角度,@SpringBootApplication 可以用哪三个注解替代?”,则答案变为 B(@Configuration + @EnableAutoConfiguration + @ComponentScan)。
【自测】
某项目启动类所在包为
com.example.app,同时在com.example.lib下有一个 @Service 类。若启动类仅标注 @SpringBootApplication 且未修改任何属性,该 Service 能否被扫描到?答:不能。因为 @ComponentScan 默认只扫描启动类所在包(com.example.app)及其子包,com.example.lib 不在此路径下。如需扫描,应显式设置
basePackages = {"com.example.app", "com.example.lib"}或使用 @ComponentScan 单独配置。与第 S02 题连考。
【知识关联】
- 同库关联:与 S02(自动配置)、S03(条件装配)、S08(启动流程)Boot 核心题群。
- 实现层:组合 @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan;主类所在包向下扫描。
- 面试追问:① 扫描不到 Bean 的原因?② 如何排除自动配置?
【拓展延伸】
- 变式问法:问三个元注解分别是什么;@ComponentScan 自定义。
- 版本差异:Spring Boot 2.x/3.x 注解稳定;3.x 基于 Spring Framework 6 / Jakarta。
- 工程注意点:主类放根包;多模块注意扫描路径;不要滥用 scanBasePackages 忘记业务包。
S02 · 知识点:自动配置原理
【题目】Spring Boot 自动配置的核心机制,是由以下哪个注解触发开启的?
A. @ComponentScan B. @EnableAutoConfiguration C. @PropertySource D. @ConfigurationProperties
答案:B
【考点】自动配置的入口注解 @EnableAutoConfiguration 及 AutoConfigurationImportSelector 的工作机制。
【结论】选 B。@EnableAutoConfiguration 是 Spring Boot 自动配置的入口注解,负责触发整个自动配置流程。
【逐项辨析】
- A. 错误。 @ComponentScan 仅负责组件扫描与注册,不参与第三方 starter 的预设配置加载。
- B. 正确。 @EnableAutoConfiguration 通过 @Import 导入 AutoConfigurationImportSelector,在容器刷新阶段读取 imports 文件并加载候选自动配置类。
- C. 错误。 @PropertySource 用于将指定的属性文件加载到 Environment 中,与自动配置类加载无关。
- D. 错误。 @ConfigurationProperties 用于将外部配置属性绑定到 POJO,属于配置消费端,而非配置触发端。
【知识点】 自动配置的完整链路可分为四个阶段:
- 触发阶段:@EnableAutoConfiguration 被解析,Spring Boot 调用 AutoConfigurationImportSelector#selectImports。
- 读取阶段:
.imports文件由ImportCandidates.load(...)(内部用ClassLoader.getResources读META-INF/spring/*.imports)读取,不是 SpringFactoriesLoader——后者只服务spring.factories(实测 3.4.6 的 spring.factories 已无EnableAutoConfiguration键,自动配置清单在 imports 文件里,共 153 行)。 文件(Spring Boot 3.x / 2.7+),逐行获取全限定类名。 - 过滤阶段:利用 Spring Boot 的 @Conditional 家族(@ConditionalOnClass、@ConditionalOnMissingBean、@ConditionalOnProperty 等)对候选类进行条件评估。不满足条件的配置类被剔除。
- 生效阶段:剩余配置类被注册到 BeanDefinitionMap,随后由容器实例化。若存在 @AutoConfigureBefore / @AutoConfigureAfter,还会按顺序调整配置类优先级。
Spring Boot 2.7 开始推荐使用 imports 文件替代 spring.factories 中的 org.springframework.boot.autoconfigure.EnableAutoConfiguration 键,以获得更快的启动性能和更清晰的结构。
【记忆锚点】 “Enable 是开关,Selector 去挑,imports 列清单,Conditional 把门看。”
【易混对比】
| 对比维度 | @EnableAutoConfiguration | @ComponentScan |
|---|---|---|
| 作用时机 | 容器 refresh 时的配置导入阶段 | 容器启动时的类路径扫描阶段 |
| 数据来源 | META-INF/spring/*.imports | 用户代码中的注解类 |
| 典型产物 | DataSourceAutoConfiguration 等 | 用户写的 @Service、@Controller |
| 排除方式 | @SpringBootApplication(exclude=...) | @ComponentScan(excludeFilters=...) |
| 对比维度 | @PropertySource | @ConfigurationProperties |
|---|---|---|
| 作用 | 加载外部 .properties 文件到 Environment | 将 Environment 中的属性绑定到 Bean |
| 使用位置 | 配置类上 | POJO 类上或 @Bean 方法参数 |
| 是否支持松散绑定 | 不涉及 | 支持 kebab/snake/camel 三种风格 |
换问法: 若题目问“Spring Boot 从哪个文件中读取候选自动配置类的全限定名?”,则应选“META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports”。
【自测】
自定义一个 starter,需在 resources/META-INF/spring/ 下新建什么文件才能让 Spring Boot 自动加载其中的配置类?该配置类又应标注什么注解才能被识别为自动配置类?
答:需新建
org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,每行列出一个自动配置类的全限定名;配置类本身应标注 @AutoConfiguration(Spring Boot 2.7+)或 @Configuration + 各类 @Conditional 条件注解。与第 S03 题连考。
【知识关联】
- 同库关联:与 S01/S03;starter 机制与 spring.factories / AutoConfiguration.imports。
- 实现层:EnableAutoConfiguration → 加载自动配置类 → @Conditional 过滤 → 注册匹配的 Bean。
- 面试追问:① 自动配置如何被触发?② 如何写自定义 starter?
【拓展延伸】
- 变式问法:
@ConditionalOnClass作用;Debug 自动配置报告。 - 版本差异:Boot 2.7+ 推荐
AutoConfiguration.imports替代 spring.factories 注册自动配置。 - 工程注意点:用
--debug/ actuator conditions 排查;自定义 starter 注意依赖与条件。
S03 · 知识点:条件装配注解
【题目】以下哪个注解可以根据配置文件(如 application.yml)中的属性值是否存在或匹配来决定是否装配 Bean?
A. @ConditionalOnClass B. @ConditionalOnMissingBean C. @ConditionalOnProperty D. @ConditionalOnBean
答案:C
【考点】@Conditional 系列条件注解的职责区分。
【结论】选 C。@ConditionalOnProperty 专用于根据外部配置文件中的属性值是否匹配来决定是否装配 Bean。
【逐项辨析】
- A. 错误。 @ConditionalOnClass 判断的是 classpath 下是否存在指定类,与配置文件无关。
- B. 错误。 @ConditionalOnMissingBean 判断的是 Spring 容器中是否不存在指定类型的 Bean,常用于提供默认实现。
- C. 正确。 @ConditionalOnProperty 通过 prefix + name(或 value)指定属性名,通过 havingValue 指定期望值,通过 matchIfMissing 控制属性缺失时的默认行为,实现基于配置开关的功能控制。
- D. 错误。 @ConditionalOnBean 判断的是容器中是否已存在指定 Bean,与配置文件无关。
【知识点】 @ConditionalOnProperty 的核心属性包括:
- prefix:属性前缀,如
app.feature。 - name / value:具体属性名数组,如
"enabled"。 - havingValue:期望的属性值,只有实际值与之相等时条件才成立。
- matchIfMissing:当属性不存在时,默认为 false(不装配);若设为 true,则属性缺失时也满足条件。
典型应用场景:
app:
feature:
enabled: true@ConditionalOnProperty(prefix = "app.feature", name = "enabled", havingValue = "true")
@Bean
public FeatureService featureService() { ... }此外,Spring Boot 还提供了 @ConditionalOnResource(判断资源文件是否存在)、@ConditionalOnWebApplication / @ConditionalOnNotWebApplication(判断应用类型)等扩展条件注解,共同构成“条件装配”体系,实现“按需加载”。
【记忆锚点】 “Property 读配置,Class 看类库,Bean 看容器,Missing 看缺位。”
【易混对比】
| 注解 | 判断依据 | 典型用途 |
|---|---|---|
| @ConditionalOnClass | classpath 中是否存在某类 | 有某依赖时才装配(如存在 Redis 才配 RedisTemplate) |
| @ConditionalOnMissingBean | 容器中是否缺少某 Bean | 提供默认实现,允许用户覆盖 |
| @ConditionalOnBean | 容器中是否已存在某 Bean | 依赖其他 Bean 存在时才装配 |
| @ConditionalOnProperty | 配置属性是否匹配 | 功能开关(如 enabled=true) |
换问法: 若题目问“当用户未自定义 RedisTemplate 时,Spring Boot 才自动创建一个默认的 RedisTemplate,这是由哪个注解实现的?”,答案应为 @ConditionalOnMissingBean。
【自测】
某配置类上标注了 @ConditionalOnProperty(prefix = "mq", name = "type", havingValue = "kafka"),而 application.yml 中仅配置了
mq.type=rabbitmq,该配置类中的 Bean 会被实例化吗?若将 matchIfMissing 设为 true 且删除 mq.type 配置,结果又如何?答:第一种情况不会实例化,因为 havingValue="kafka" 与实际值 "rabbitmq" 不匹配;第二种情况会实例化,因为 matchIfMissing=true 时属性缺失视为条件成立。与第 S04 题连考。
【知识关联】
- 同库关联:与 S02;@ConditionalOnBean/Class/MissingBean/Property。
- 实现层:Condition 接口在配置解析阶段求值。
- 面试追问:① 条件评估时机?② 顺序与覆盖?
【拓展延伸】
- 变式问法:用户自定义 Bean 如何覆盖自动配置(@ConditionalOnMissingBean)。
- 版本差异:注解稳定。
- 工程注意点:条件注解要可预期;避免隐式难排查的开关。
S04 · 知识点:配置文件优先级
【题目】在 Spring Boot 中,同一目录下同时存在 application.properties 与 application.yml,且二者配置了同名属性,最终生效的是?
A. 随机加载其中一个 B. application.yml 中的配置 C. 二者报错,启动失败 D. application.properties 中的配置
答案:D
【考点】配置文件格式的加载顺序与同名属性覆盖规则。
【结论】选 D。当同一目录下同时存在 application.properties 与 application.yml 时,application.properties 的优先级更高,同名属性以其为准。
【逐项辨析】
- D. 正确。 Spring Boot 官方文档明确规定,同一位置的 application.properties 优先级高于 application.yml(以及 .yaml)。
- B. 错误。 application.yml 虽然也会被加载,但在同名属性冲突时会被 properties 文件覆盖。
- C. 错误。 二者不存在冲突报错机制,Spring Boot 会平滑合并所有来源的配置,按优先级规则覆盖同名项。
- A. 错误。 加载顺序由框架严格定义,不存在随机性。
【知识点】 Spring Boot 的配置是一个“多源合并、优先级覆盖”的体系。官方优先级从高到低(节选)依次为:
- 命令行参数(
--server.port=9090) - Java 系统属性(
System.getProperties()) - 操作系统环境变量
- jar 包外部的 application-{profile}.properties / .yml
- jar 包外部的 application.properties / .yml
- jar 包内部的 application-{profile}.properties / .yml
- jar 包内部的 application.properties / .yml
- @PropertySource 加载的属性
- SpringApplication 的默认属性
在同一层级内部,properties 始终优先于 yml。这意味着若 jar 内部同时存在 application.properties 和 application.yml,properties 中同名属性会覆盖 yml。而若 jar 外部存在 application.yml,其优先级仍高于 jar 内部的 application.properties(因为“外部优于内部”的层级规则优先于“格式优先”规则)。
【记忆锚点】 “同层 properties 压 yml,外部配置压内部,命令行是王者。”
【易混对比】
| 场景 | 生效来源 | 原因 |
|---|---|---|
| 同目录 application.properties vs application.yml | application.properties | 格式优先级:properties > yml |
| jar 外部 application.yml vs jar 内部 application.properties | jar 外部 application.yml | 位置优先级:外部 > 内部 |
| application-prod.yml vs application.yml | application-prod.yml | Profile 专属文件覆盖默认文件 |
| 命令行参数 vs 环境变量 | 命令行参数 | 命令行参数优先级最高 |
换问法: 若题目问“将 application.properties 放在 jar 包同级目录下,application.yml 放在 jar 包内 classpath 根目录,同名属性以谁为准?”,答案应为 jar 包外部的 application.properties(外部位置优先)。
【自测】
项目中 jar 包内部同时存在 application.properties(server.port=8080)和 application.yml(server.port: 9090),且 jar 包外部存在 application.yml(server.port: 7070)。启动后实际监听端口是多少?
答:7070。解析:外部 application.yml 优先级高于内部 application.properties(外部位置优先规则 > 格式优先规则)。与第 S05 题连考。
【知识关联】
- 同库关联:与 S05(绑定)、S06(Profile)、S29(配置中心)。
- 实现层:Environment 抽象多 PropertySource;命令行 > 系统环境 > application-{profile}.yml > application.yml 等(有文档优先级)。
- 面试追问:① 外部化配置优先级?② 如何覆盖?
【拓展延伸】
- 变式问法:
--server.port=与 yml 谁优先。 - 版本差异:细节以对应版本官方文档为准。
- 工程注意点:敏感配置走环境变量/配置中心;本地 profile 与生产隔离。
S05 · 知识点:@ConfigurationProperties 宽松绑定
【题目】使用 @ConfigurationProperties(prefix = "student") 将配置绑定到字段 schoolName 时,以下哪个 yml 写法可以被正确绑定?
A. student.school-name: xxx B. student.school_name: xxx C. student.schoolName: xxx D. 以上都可以
答案:D
【考点】@ConfigurationProperties 的宽松绑定(Relaxed Binding)规则。
【结论】选 D。@ConfigurationProperties 支持宽松绑定,kebab-case、snake_case、camelCase 三种写法均可正确映射到 schoolName 字段。
【逐项辨析】
- A. 正确。 kebab-case(短横线连接)是 Spring Boot 官方推荐的外部配置书写风格,可被宽松绑定解析。
- B. 正确。 snake_case(下划线连接)同样属于宽松绑定支持的命名风格。
- C. 正确。 camelCase(驼峰命名)与 Java 字段名完全一致,当然可以被绑定。
- D. 正确。 由于 A、B、C 均可独立成立,故“以上都可以”是最终答案。
【知识点】 宽松绑定(Relaxed Binding)是 @ConfigurationProperties 的核心特性之一,其规则如下:
- 目标字段命名:绑定到 Java 字段时,字段名本身的驼峰格式会被拆解为单词片段(school + name)。
- 源属性匹配:配置源(yml / properties / 环境变量)中的属性名可以是 kebab-case(
school-name)、snake_case(school_name)、camelCase(schoolName)或 大写下划线(SCHOOL_NAME,环境变量常见)。 - 大小写不敏感:匹配过程中不区分大小写,且分隔符(-、_、空)被视为等价。
- @Value 的限制:@Value("${student.schoolName}") 不支持宽松绑定,必须严格匹配属性名。这也是推荐使用 @ConfigurationProperties 进行批量配置绑定的重要原因。
此外,若 prefix 本身包含多层,如 app.student-info,则对应字段名 studentInfo 也能通过宽松绑定正确映射。
【记忆锚点】 “中划线、下划线、驼峰线,宽松绑定三通吃;@Value 是死脑筋,只认原配不认亲戚。”
【易混对比】
| 对比维度 | @ConfigurationProperties | @Value |
|---|---|---|
| 松散绑定 | 支持 kebab / snake / camel / 大写下划线 | 不支持,必须严格匹配 |
| 批量绑定 | 支持,整个对象一次性注入 | 不支持,需逐个字段指定 |
| 类型转换 | 支持复杂类型(List、Map、Duration) | 基础类型简单转换 |
| JSR-303 校验 | 可与 @Validated + @NotNull 等联用 | 不直接支持 |
| SpEL 表达式 | 不支持 | 支持 ${} 及 #{} |
| 使用场景 | 结构化配置、多字段绑定 | 单个属性、动态表达式 |
换问法: 若题目问“以下哪种写法不能被 @Value(”${student.schoolName}“) 正确解析?”,则 student.school-name 和 student.school_name 均无法被识别,只有严格匹配 student.schoolName 才可以。
【自测】
某配置类使用 @ConfigurationProperties(prefix = "mail") 绑定字段
smtpHost,以下哪些 properties 写法可以成功注入? ① mail.smtp-host=xxx ② mail.smtp_host=xxx ③ mail.smtphost=xxx ④ mail.SMTP_HOST=xxx答:①②③④ 四种写法全部可以成功注入,其中就包括 ③
mail.smtphost。宽松绑定比较属性名时用的是 uniform 形式:先去掉-、_等分隔符,再忽略大小写做比对,因此smtp-host、smtp_host、smtphost、SMPTHOST、smtpHost在绑定阶段是同一个名字;并不存在「单词边界必须有分隔符」这条规则——原答案把 ③ 判为无法匹配,方向正好说反了。
SpringBoot = 3.4.6,目标字段 smtpHost(Binder + MapConfigurationPropertySource 实测)
mail.smtp-host -> 绑定成功: smtpHost=smtp.example.com
mail.smtp_host -> 绑定成功: smtpHost=smtp.example.com
mail.smtphost -> 绑定成功: smtpHost=smtp.example.com ← 旧答案误判为「无法匹配」
mail.SMTP_HOST -> 绑定成功: smtpHost=smtp.example.com
mail.SMTPHOST -> 绑定成功: smtpHost=smtp.example.com
mail.smtpHost -> 绑定成功: smtpHost=smtp.example.com复现方式:
new Binder(new MapConfigurationPropertySource(map)).bind("mail", Mail.class),map 中每次只放上面一个 key。另需分清边界:宽松绑定只服务于@ConfigurationProperties,@Value("${mail.smtp-host}")必须与配置文件里写的名字严格一致(见本题【知识点】第 4 条)。与第 S06 题连考。
【知识关联】
- 同库关联:与 S04;@ConfigurationProperties vs @Value。
- 实现层:宽松绑定(kebab/camel/relaxed);JSR-303 校验可选。
- 面试追问:① 与 @Value 差异?② 前缀规范?
【拓展延伸】
- 变式问法:
app.user-name绑定 userName。 - 版本差异:宽松绑定规则 Boot2/3 有收紧趋势,命名尽量规范。
- 工程注意点:结构化配置用 @ConfigurationProperties + POJO;加 @Validated。
S06 · 知识点:Profile 多环境配置
【题目】在 application.yml 中设置 spring.profiles.active=prod 后,Spring Boot 启动时会加载哪些配置文件?
A. 仅加载 application-prod.yml B. 启动报错,需删除默认配置文件 C. 仅加载 application.yml D. 加载 application.yml 与 application-prod.yml,且 prod 文件覆盖默认配置
答案:D
【考点】Profile 的加载机制与同名配置覆盖关系。
【结论】选 D。Spring Boot 会同时加载默认配置文件 application.yml 和 Profile 专属文件 application-prod.yml,并以 Profile 文件中的同名配置覆盖默认配置。
【逐项辨析】
- A. 错误。 不会仅加载 Profile 文件,默认配置是全局基础,始终会被加载。
- D. 正确。 默认配置与 Profile 配置合并加载,Profile 专属文件在合并层中优先级更高,实现同名属性覆盖。
- C. 错误。 若仅加载默认配置,则 Profile 机制失去意义,无法完成多环境隔离。
- B. 错误。 Spring Boot 允许并鼓励同时保留默认配置和多个 Profile 文件,框架会正常处理,不会报错。
【知识点】 Profile 机制的设计哲学是“默认打底、环境覆盖”。具体流程如下:
- 解析 active profiles:Spring Boot 从
spring.profiles.active(或spring.profiles.default)获取待激活的 Profile 列表,支持多 Profile 同时激活(如dev,test)。 - 加载默认配置:首先加载 application.properties / application.yml 中的非 Profile 特定配置。
- 加载 Profile 专属配置:按顺序加载 application-{profile}.properties / application-{profile}.yml。若多个 Profile 同时激活,后加载的 Profile 优先级更高(会覆盖前者同名属性)。
- 合并与覆盖:所有配置来源按 Spring Boot 的优先级规则合并成最终的 Environment,Profile 专属配置处于较高层级。
补充:spring.config.activate.on-profile(在单文件内分隔 Profile 块)与 spring.profiles.group(将多个 Profile 组合为一个逻辑组)都是 Spring Boot 2.4 随配置文件处理机制重构一起引入的——2.4 同时引入了 spring.config.import、把多文档块里旧的 spring.profiles: xxx 写法换成了 spring.config.activate.on-profile(见本题【拓展延伸】「2.4+ 配置文件处理有 breaking change」)。不是 3.1 才有的特性,3.x 只是延续 2.4+ 的这套模型;把版本写成 3.1 与本题【拓展延伸】自相矛盾,应以 2.4 为准。
【记忆锚点】 “默认打底,Profile 盖楼;active 是开关,同名后者优。”
【易混对比】
| 对比维度 | 默认配置(application.yml) | Profile 专属配置(application-prod.yml) |
|---|---|---|
| 加载时机 | 始终加载 | 仅在对应 Profile 激活时加载 |
| 优先级 | 较低 | 较高(覆盖默认同名项) |
| 用途 | 存放通用、跨环境共享配置 | 存放环境特定配置(如数据库地址、日志级别) |
| 是否必须 | 推荐保留 | 按需创建 |
| 属性 | 作用 |
|---|---|
| spring.profiles.active | 显式激活指定 Profile(最高优先级) |
| spring.profiles.default | 未指定 active 时的默认兜底 Profile |
| spring.profiles.include | 在已激活 Profile 基础上追加额外 Profile |
| spring.profiles.group | 将多个 Profile 绑定为一个组名 |
换问法: 若题目问“同时激活 dev 和 prod 两个 Profile,且二者存在同名属性,最终以谁为准?”,答案为后加载的 Profile(取决于声明顺序,通常后声明的优先级更高)。
【自测】
某项目 application.yml 中配置了
spring.profiles.active: dev,mysql,且存在 application-dev.yml 和 application-mysql.yml。若 application-dev.yml 中app.name=dev-app,application-mysql.yml 中app.name=mysql-app,最终 Environment 中 app.name 的值是多少?答:mysql-app。多个 Profile 同时激活时,后加载的 Profile(mysql)优先级高于先加载的(dev),同名属性以后者为准。与第 S07 题连考。
【知识关联】
- 同库关联:与 S04;spring.profiles.active / include。
- 实现层:Profile 条件激活不同 PropertySource 与 @Profile Bean。
- 面试追问:① 如何激活?② 生产默认 profile?
【拓展延伸】
- 变式问法:
@Profile("dev");启动参数--spring.profiles.active=prod。 - 版本差异:2.4+ 配置文件处理有 breaking change(spring.config.import 等)。
- 工程注意点:禁止把密钥打进默认配置;配置差异用 profile 拆分。
S07 · 知识点:内嵌 Web 容器
【题目】spring-boot-starter-web 默认引入的内嵌 Web 容器及其默认端口是? A. Jetty,8080 B. Tomcat,8080 C. Undertow,8081 D. Tomcat,8081
答案:B
【考点】Spring Boot 默认内嵌容器与默认端口配置。
【结论】选 B。spring-boot-starter-web 默认引入内嵌 Tomcat,默认监听端口为 8080。
【逐项辨析】
- A. 错误。 Jetty 是可选替换容器,非默认引入。
- B. 正确。 spring-boot-starter-web 的传递依赖中默认包含 spring-boot-starter-tomcat,且 server.port 默认值是 8080。
- C. 错误。 Undertow 同样是可选替换容器,且默认端口不是 8081。
- D. 错误。 容器正确但端口错误,Spring Boot 默认端口为 8080,除非显式修改。
【知识点】 spring-boot-starter-web 的依赖树核心路径为:
spring-boot-starter-web
└── spring-boot-starter-tomcat
└── tomcat-embed-core / tomcat-embed-websocket如需切换容器,需在 pom.xml 中排除 Tomcat 并引入替代 starter:
- 切换 Jetty:排除
spring-boot-starter-tomcat,引入spring-boot-starter-jetty。 - 切换 Undertow:排除
spring-boot-starter-tomcat,引入spring-boot-starter-undertow。
端口配置方式:
server.port=9090(properties)server.port: 9090(yml)- 命令行
--server.port=9090 - 随机端口:
server.port=0(实际端口在运行时分配,可通过 @LocalServerPort 获取)
此外,server.* 命名空间还包含 server.servlet.context-path、server.tomcat.max-threads 等大量容器调优参数。
【记忆锚点】 “Web 默认 Tomcat,端口 8080 忘不掉;想换 Jetty 或 Undertow,先排 Tomcat 再引包。”
【易混对比】
| 容器 | 特点 | 适用场景 |
|---|---|---|
| Tomcat | 默认、成熟、社区最大、Servlet 规范完整实现 | 通用场景,开发首选 |
| Jetty | 轻量、异步支持好、嵌入式历史悠久 | 长连接、WebSocket、高并发 |
| Undertow | 基于 XNIO、内存占用低、支持阻塞/非阻塞混合 | 资源受限、微服务、云原生 |
| 配置项 | 默认值 | 说明 |
|---|---|---|
| server.port | 8080 | HTTP 监听端口 |
| server.servlet.context-path | (无默认值,等价于 /) | 元数据里该项没有默认值,不配即根路径 |
| server.tomcat.threads.max | 200 | Tomcat 最大线程数 |
| server.jetty.threads.max | 200 | Jetty 最大线程数 |
| server.undertow.threads.worker | CPU 核心数 × 8 | Undertow 工作线程数 |
换问法: 若题目问“如何在不修改配置文件的情况下将端口临时改为 9090?”,答案应为启动时传入命令行参数 --server.port=9090。
【自测】
某微服务需要在一台机器上启动多个实例,要求每个实例自动分配不同端口且互不冲突,应如何配置?又如何获取实际分配的端口?
答:在配置文件中设置
server.port=0,Spring Boot 会委托操作系统分配一个可用临时端口;在代码中通过@LocalServerPort(或@Value("${local.server.port}"))注入实际端口号,或从WebServerApplicationContext中获取。与第 S08 题连考。
【知识关联】
- 同库关联:与 S08;内嵌 Tomcat/Jetty/Undertow。
- 实现层:Servlet 容器作为依赖自动配置;
spring-boot-starter-web默认 Tomcat。 - 面试追问:① 如何换成 Jetty?② 打 war 外置容器?
【拓展延伸】
- 变式问法:排除 tomcat 依赖引入 jetty。
- 版本差异:Boot3 对应 Servlet 6 / Tomcat 10+(Jakarta)。
- 工程注意点:容器版本与 Boot 版本匹配;K8s 探针与优雅停机。
S08 · 知识点:Spring Boot 启动流程
【题目】关于 SpringApplication.run() 的启动流程,以下顺序正确的是?
A. 推断应用类型 → 创建 SpringApplication → 准备 Environment → 创建容器 → refresh B. 创建 SpringApplication → 推断应用类型 → 准备 Environment → 创建并刷新容器 → 启动完成 C. 创建容器 → 推断应用类型 → 创建 SpringApplication → 加载配置 D. 以上顺序均不正确
答案:B
【考点】Spring Boot 启动流程的主要阶段顺序。
【结论】选 B。完整顺序为:创建 SpringApplication 实例(内部推断应用类型)→ 准备 Environment → 创建 ApplicationContext → refresh 刷新容器 → 启动完成。
【逐项辨析】
- A. 错误。 推断应用类型发生在 new SpringApplication() 的构造过程中,而非创建实例之前,整体顺序描述不准确。
- B. 正确。 main 方法中先
new SpringApplication()(构造阶段推断应用类型、加载初始化器和监听器),再调用run()完成环境准备、容器创建与刷新、Runner 执行。 - C. 错误。 创建容器是 run() 方法中较后的步骤,不可能先于 SpringApplication 的创建。
- D. 错误。 选项 B 的描述符合官方源码逻辑。
【知识点】 Spring Boot 启动流程可细分为两大阶段:
阶段一:构造 SpringApplication
- 推断应用类型(WebApplicationType):
- SERVLET:classpath 存在
jakarta.servlet.Servlet(Boot 3 / Framework 6 已切到 Jakarta 命名空间,实测WebApplicationType常量池里只有jakarta/servlet/Servlet,无 javax)且不存在 Reactive Web 栈。老Boot 2.x 才是javax.servlet.Servlet。 - REACTIVE:classpath 存在 org.springframework.web.reactive.DispatcherHandler 且不存在 Servlet。
- NONE:以上均不满足。
- SERVLET:classpath 存在
- 加载
ApplicationContextInitializer和ApplicationListener(通过 SpringFactoriesLoader 读取 META-INF/spring.factories)。 - 推断主类(main 方法所在类)。
阶段二:执行 run() 方法
- 获取并启动
SpringApplicationRunListeners(发布 ApplicationStartingEvent)。 - 准备
Environment(加载配置文件、解析命令行参数、绑定到 Spring 环境)。 - 打印 Banner(默认 Spring Boot 图案,可自定义)。
- 创建
ApplicationContext(根据应用类型创建 AnnotationConfigServletWebServerApplicationContext 或 Reactive 对应上下文)。 - 准备 Context(执行 Initializer、加载源配置类、发布 ApplicationPreparedEvent)。
- 刷新 Context(refresh()):
- 注册 BeanDefinition
- 触发自动配置(@EnableAutoConfiguration)
- 实例化单例 Bean
- 启动内嵌 Web 服务器(若存在)
- 执行
CommandLineRunner和ApplicationRunner。 - 发布
ApplicationReadyEvent,启动完成。
【记忆锚点】 “先建应用再推断,准备环境后刷新;Runner 最后跑起来,Ready 事件表完成。”
【易混对比】
| 应用类型 | 判断条件 | 创建的上下文 |
|---|---|---|
| SERVLET | 存在 Servlet API,无 Reactive | AnnotationConfigServletWebServerApplicationContext |
| REACTIVE | 存在 WebFlux,无 Servlet | AnnotationConfigReactiveWebServerApplicationContext |
| NONE | 无 Web 依赖 | AnnotationConfigApplicationContext |
| 阶段 | 关键动作 | 对应事件 |
|---|---|---|
| 构造期 | 推断类型、加载 Initializer/Listener | ApplicationStartingEvent |
| 环境准备 | 加载配置文件、解析参数 | ApplicationEnvironmentPreparedEvent |
| Context 准备 | 执行 Initializer、注册主类 | ApplicationPreparedEvent |
| Context 刷新 | 自动配置、实例化 Bean、启动 Web 容器 | — |
| 启动完成 | 执行 Runner | ApplicationReadyEvent / ApplicationFailedEvent |
换问法: 若题目问“在 Spring Boot 启动过程中,内嵌 Tomcat 是在哪个阶段被启动的?”,答案应为 refresh Context 阶段(具体在 onRefresh() 或 finishRefresh() 中通过 WebServerStartStopLifecycle 启动)。
【自测】
如何在 Spring Boot 启动完成后、接收第一个 HTTP 请求前,执行一段初始化逻辑(如缓存预热、数据加载)?请写出代码片段并说明原理。
答:实现
ApplicationRunner或CommandLineRunner接口,将其注册为 Bean,Spring Boot 在容器刷新完成后会自动回调。示例:
@Component
public class CacheWarmUpRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) {
// 执行缓存预热逻辑
}
}原理:SpringApplication.run() 在 refreshContext 之后、发布 ApplicationReadyEvent 之前,会遍历容器中所有 Runner 并依次调用。与第 S01 题连考。
【知识关联】
- 同库关联:与 S01/S02;SpringApplication.run 阶段。
- 实现层:准备 Environment → 创建 Context → 刷新(加载配置、自动配置、Bean) → Runner 回调。
- 面试追问:① 监听器有哪些扩展点?② 启动慢如何排查?
【拓展延伸】
- 变式问法:ApplicationRunner/CommandLineRunner 区别。
- 版本差异:细节随版本演进。
- 工程注意点:避免在 @PostConstruct 做重活;启动分析用 actuator startup。