SpringBoot面试题
Spring Boot 启动流程
面试官您好,我来梳理一下 Spring Boot 的完整启动流程。它本质上是 "一个 main 方法 + 两个核心阶段",把传统 Spring 的复杂配置全部自动化了,我从源码角度给您拆解一下👇
核心流程图 📊
准备环境 → 创建容器 → 刷新容器 → 启动内嵌 Web 服务器
启动流程全景图
SpringApplication.run()
│
▼
┌─────────────────┐
│ 1.推断应用类型 │ → Servlet ? Reactive ? 普通 ?
└────────┬────────┘
▼
┌─────────────────┐
│ 2.加载初始化器 │ → ApplicationContextInitializer
│ & 监听器 │ → ApplicationListener
└────────┬────────┘
▼
┌─────────────────┐
│ 3.准备环境 │ → Environment (配置、profile等)
└────────┬────────┘
▼
┌─────────────────┐
│ 4.创建容器 │ → AnnotationConfigServletWebServerApplicationContext 等
└────────┬────────┘
▼
┌─────────────────┐
│ 5.准备容器 │ → setEnvironment、applyInitializers
└────────┬────────┘
▼
┌─────────────────┐
│ 6.刷新容器 🔥 │ → AbstractApplicationContext.refresh() (灵魂所在)
└────────┬────────┘
▼
┌─────────────────┐
│ 7.启动 WebServer │ → Tomcat/Jetty/Undertow 内嵌启动
└────────┬────────┘
▼
[应用启动完成✅]分阶段详细拆解 ✨
阶段 1:SpringApplication 对象初始化(构造方法)
这一步是 "提前准备好所有工具",为后续启动做铺垫:
- 推断应用类型:根据
classpath中是否存在DispatcherServlet判断是Servlet还是Reactive应用- 有
DispatcherServlet→ Servlet Web 应用 - 有
DispatcherHandler且没有DispatcherServlet→ Reactive Web 应用 - 都没有 → 普通 Java 应用
- 这步决定了后面创建哪种
ApplicationContext。
- 这步决定了后面创建哪种
- 有
- 加载初始化器:通过
SpringFactoriesLoader从META-INF/spring.factories中加载所有ApplicationContextInitializer - 加载监听器:通过
SpringFactoriesLoader从META-INF/spring.factories中加载所有ApplicationListener - 推断主类:通过堆栈信息找到包含
main方法的启动类
阶段 2:run () 方法执行(核心流程)
这一步是 "真正启动 Spring 容器",其中refresh()方法是灵魂:
- 启动计时:用
StopWatch统计启动耗时 - 配置 Headless 模式:设置
java.awt.headless=true,适配服务器无图形界面环境 - 获取运行监听器:加载
SpringApplicationRunListener,用于在启动各节点发布事件 - 准备环境:加载配置文件(
application.yml等)、系统属性、环境变量 - 打印 Banner:就是我们启动时看到的那个 Spring Boot 大 logo
- 创建 ApplicationContext:根据应用类型创建对应的 IOC 容器
- 准备容器:把环境、监听器、初始化器注入到容器中
- 执行 refresh ():最核心的一步,完成 Bean 的扫描、解析、实例化和初始化
- 执行钩子方法:调用
CommandLineRunner和ApplicationRunner - 发布就绪事件:标志着 Spring Boot 应用完全启动成功
refresh()
├─ prepareRefresh() // 准备上下文 (记录启动时间、校验必须属性)
├─ obtainFreshBeanFactory() // 解析配置类,扫描包路径,加载所有 Bean 定义
├─ prepareBeanFactory() // 注册特殊 Bean (environment, systemProperties等)
├─ invokeBeanFactoryPostProcessors() // 💥 执行 BeanFactory 后处理器 (包括 ConfigurationClassPostProcessor)
├─ registerBeanPostProcessors() // 注册 Bean 后处理器 (AOP, 自动代理都在这埋点)
├─ initMessageSource() // 国际化
├─ initApplicationEventMulticaster() // 事件广播器
├─ onRefresh() // 👉 留给子类的钩子:内嵌 Web 容器就在这创建
├─ registerListeners() // 注册监听器
├─ finishBeanFactoryInitialization() // 💥 单例池初始化 (所有非懒加载单例 Bean 真正创建)
└─ finishRefresh() // 清缓存,启动 WebServer,发 ContextRefreshedEvent🔥 关键点:
ConfigurationClassPostProcessor负责解析@Configuration、@ComponentScan、@Bean等,把所有 Bean 定义注册进来。onRefresh()里头,ServletWebServerApplicationContext 会创建一个 Web Server 工厂,等着最后启动。finishBeanFactoryInitialization()是 面试高频区,所有业务单例 Bean 的依赖注入、初始化都是这里完成的(getBean()触发三级缓存解决循环依赖)。
🧩 一张更生动的时序图
面试必问关键点 🔑
自动配置在哪里触发?
在refresh()方法的invokeBeanFactoryPostProcessors()步骤中,会调用ConfigurationClassPostProcessor,进而触发@EnableAutoConfiguration注解的自动配置逻辑。
spring.factories 文件的作用?
Spring Boot 的 SPI 机制,所有自动配置类、初始化器、监听器都在这里注册,Spring Boot 启动时会自动扫描并加载。
CommandLineRunner 和 ApplicationRunner 的区别?
都是在容器启动完成后执行,区别在于参数类型:
CommandLineRunner:接收原始字符串数组参数ApplicationRunner:接收封装后的ApplicationArguments对象,更方便解析参数
加分项回答 💯
| 阶段 | 关键字 | 面试官爱问 |
|---|---|---|
| 初始准备 | SpringFactoriesLoader | SPI 机制怎么加载自动配置 |
| 环境 | Environment、PropertySource | 配置优先级、多环境切换 |
| 容器刷新 | refresh()、BeanFactoryPostProcessor | ConfigurationClassPostProcessor 原理 |
| 单例创建 | finishBeanFactoryInitialization | 循环依赖三级缓存 |
| Web 启动 | onRefresh()、WebServer | 怎么切换 Jetty/Undertow |
| 事件 | ApplicationEvent | 监听器执行顺序、自定义启动事件 |
面试官,我还想补充一点:Spring Boot 的启动流程设计非常优雅,全程基于事件驱动和SPI 扩展机制,这让我们可以非常方便地在启动的各个节点插入自定义逻辑。比如我们可以通过实现ApplicationListener来监听启动事件,或者通过实现ApplicationContextInitializer来在容器刷新前做一些自定义配置。
Spring Boot 自动配置原理
面试官您好,我来回答一下 Spring Boot 自动配置原理。这个可以说是 Spring Boot 最核心的特性,也是它能 "约定大于配置" 的根本原因。
其实自动配置说白了就是 Spring Boot 帮我们“猜”需要用哪些 Bean,然后自动装配好,省得我们自己写一堆 xml 或配置类。它的核心入口就在 @SpringBootApplication 这个注解上。
这个注解是个组合拳,里面包含三个重要角色:
@SpringBootConfiguration // 本质就是 @Configuration
@EnableAutoConfiguration // 🚀 自动配置的主角
@ComponentScan // 组件扫描我们关心的就是 @EnableAutoConfiguration,它会通过 @Import 引入一个关键类 AutoConfigurationImportSelector,这个类是整个自动配置的“调度中心”。
整个流程可以用下面这张图概括 📊:
往下再细化一下,主要有这些步骤:
- 加载候选配置
AutoConfigurationImportSelector 通过 SpringFactoriesLoader 去所有 jar 包的 META-INF/spring.factories 文件中,读取 org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的所有配置类列表。
比如 spring-boot-autoconfigure 这个 jar 里就有上百个 XXXAutoConfiguration。
- 条件过滤 ⚖️
读取到的候选类并不会全部生效,Spring Boot 用了一堆条件注解来按需启用:
@ConditionalOnClass—— 类路径里是否有某个类,比如有 HikariCP 才配数据源@ConditionalOnMissingBean—— 容器里是否没这个 Bean,有就不重复配@ConditionalOnProperty—— 配置文件里是否有某属性@ConditionalOnWebApplication—— 是不是 Web 应用- 等等……
- 自动注册 Bean
经过条件筛选后,剩下的配置类会被当做普通的 @Configuration 类处理,里面的 @Bean 方法就会被执行,把需要的组件(如 DataSource、RabbitTemplate、RedisTemplate)装进容器。
举个真实的例子 🌰:DataSourceAutoConfiguration
- 注解上有
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })——意味着类路径必须有这两个类。 - 内部还会判断连接池类型,比如有 HikariCP 就创建 HikariDataSource。
- 用
@ConditionalOnMissingBean保证你只要自己定义了数据源,它就不再自动创建了。
那如何自己扩展或关闭自动配置呢? 👇
- 排除特定配置类:
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) - 完全控制:设
spring.autoconfigure.exclude属性列表。 - 自定义 starter:写自己的
META-INF/spring.factories,加上条件注解,就能让 Spring Boot 发现并自动装配。
说到底,自动配置就是个“约定大于配置”的体现,核心套路就是 spring.factories 注册 + 条件注解筛选。平时开发时,点开这些 AutoConfiguration 类看看,比背八股文强得多 😄。
一句话总结核心思想 🎯
Spring Boot 会根据你引入的 jar 包依赖,自动地、智能地为你配置好 Spring 应用上下文里的 Bean,无需手动编写大量 XML 或 Java 配置。
自动配置的三大核心组件 🧩
| 组件 | 作用 | 关键文件 / 注解 |
|---|---|---|
| 条件注解 | 决定哪些配置类需要被加载 | @ConditionalOnClass、@ConditionalOnMissingBean、@ConditionalOnProperty |
| SPI 机制 | 扫描所有 jar 包下的自动配置类 | META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports |
| 配置绑定 | 将 application.yml 中的属性绑定到 JavaBean | @ConfigurationProperties、@EnableConfigurationProperties |
自动配置完整执行流程图 📊
Spring Boot应用启动
↓
加载@SpringBootApplication复合注解
↓
触发@EnableAutoConfiguration自动配置开关
↓
导入AutoConfigurationImportSelector选择器
↓
通过SPI扫描所有jar包的AutoConfiguration.imports文件
↓
获取所有候选自动配置类列表
↓
根据@Conditional系列条件注解过滤
↓
加载符合条件的自动配置类
↓
将配置类中定义的Bean注册到Spring容器
↓
将application.yml属性绑定到@ConfigurationProperties类
↓
✅ 自动配置完成最关键的注解解析 🔍
1. @SpringBootApplication
这是一个复合注解,包含三个核心注解:
@SpringBootConfiguration:标记为配置类@ComponentScan:扫描主类所在包及其子包下的组件@EnableAutoConfiguration:开启自动配置的开关 ✨
2. @EnableAutoConfiguration
这个注解才是自动配置的真正入口,它通过@Import(AutoConfigurationImportSelector.class)导入了自动配置选择器,这个选择器会去读取 SPI 文件中的所有自动配置类。
3. 条件注解举例
// 当类路径下存在RedisTemplate类时才加载这个配置
@ConditionalOnClass(RedisTemplate.class)
// 当容器中没有名为redisTemplate的Bean时才创建
@ConditionalOnMissingBean(name = "redisTemplate")
// 当配置文件中spring.redis.enabled=true时才加载
@ConditionalOnProperty(prefix = "spring.redis", name = "enabled", havingValue = "true")
public class RedisAutoConfiguration {
// ...
}自定义自动配置的步骤 🛠️
- 创建自动配置类,添加
@Configuration注解 - 使用条件注解控制配置加载时机
- 在
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中注册配置类 - 打包成 starter 供其他项目使用
面试官常问的加分点 💯
- 自动配置类的加载顺序:可以通过
@AutoConfigureBefore、@AutoConfigureAfter、@AutoConfigureOrder控制 - 排除自动配置:
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) - 查看自动配置报告:启动时添加
--debug参数,可以看到哪些自动配置生效了,哪些被排除了 - Spring Boot 3.x 变化:SPI 文件从
META-INF/spring.factories改为了AutoConfiguration.imports文件
Spring Boot Starter 机制与常用 Starter 🚀
面试官您好,我来回答一下 Spring Boot Starter 机制和常用 Starter 的问题。
🧑💻 一次接地气的回答
“面试官好,我觉得 Starter 说白了就是 ‘一键引入一组依赖 + 自动搞定配置’ 的模块打包方案。
以前用 Spring MVC,我得在 pom 里引 spring-webmvc、jackson、tomcat 一大堆 jar,还要 XML 配 DispatcherServlet、视图解析器,很容易漏版本、冲突。现在呢?一个 spring-boot-starter-web,依赖全管好,还通过自动配置,根据 classpath 里的类、已有的 Bean、配置文件的值,帮你把缺省 Bean 都给装配上了,开箱即用,咱们只需按需覆盖。
它的核心机制就是:‘依赖管理 + 自动配置 + 条件装配’ 的组合拳。我只要 starter 的 jar 在依赖里,Spring Boot 启动时通过 spring.factories 或新版的 AutoConfiguration.imports 加载自动配置类,用 @Conditional 系列注解判断是否生效,满足条件就注入默认 Bean。这样实现了约定大于配置。”
Starter 到底是什么?🤔
Starter 是 Spring Boot 的核心设计思想之一,本质上是一个 "依赖描述符" + "自动配置类" 的组合包。
它解决了传统 Spring 开发中 "依赖地狱"和"配置繁琐" 两大痛点:
- 不用再手动导入一堆零散的 jar 包和处理版本冲突
- 不用再写大量重复的 XML 或 Java 配置
- 开箱即用,只需要引入一个 starter 依赖,就能获得完整的功能支持
Starter 核心工作机制 ⚙️
1. 整体流程
Starter 的核心是自动配置,整个流程可以用这张图清晰展示:
2. 关键注解详解
| 注解 | 作用 | 核心逻辑 |
|---|---|---|
@SpringBootApplication | 启动类注解 | 组合了@Configuration+@EnableAutoConfiguration+@ComponentScan |
@EnableAutoConfiguration | 开启自动配置 | 核心!通过SpringFactoriesLoader加载所有自动配置类 |
@Conditional系列 | 条件装配 | 只有满足特定条件才创建 Bean |
3. 最常用的条件注解 ✨
@ConditionalOnClass:classpath 下存在指定类时生效@ConditionalOnMissingBean:容器中不存在指定 Bean 时才创建@ConditionalOnProperty:配置文件中存在指定属性时生效@ConditionalOnWebApplication:Web 应用环境下生效
🔧 核心机制拆解(有料版)
1. 依赖传递与版本仲裁
每个 Starter 本身可能是个空 jar,但它通过 pom.xml 帮你把相关依赖都拉进来,并且版本由 Spring Boot 的父 POM 统一管控,避免冲突。
📦 Starter 就是一个打包清单,真正的魔法在 spring-boot-autoconfigure 里。
2. 自动配置的加载入口
- 旧版(2.x):
META-INF/spring.factories的org.springframework.boot.autoconfigure.EnableAutoConfiguration键。 - 新版(2.7+ 推荐,3.0 默认):
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,每行一个配置类全限定名。
启动时,@EnableAutoConfiguration 会触发 AutoConfigurationImportSelector 去读取这些文件,得到候选配置类列表。
3. 条件装配(@Conditional 系列)⭐
自动配置类上会有各种条件注解,保证“缺啥补啥,有则不冲突”:
@ConditionalOnClass:类存在才生效(比如 DataSource 在 classpath 才配数据源)@ConditionalOnMissingBean:用户没自定义 Bean 时,才用默认的@ConditionalOnProperty:配置文件有特定属性才激活@ConditionalOnBean:存在某个 Bean 才装配
4. 配置属性绑定
@ConfigurationProperties 把 application.yml 里的前缀属性映射到 Bean,结合 @EnableConfigurationProperties 激活,这样就可以用外部配置轻易调整行为。
自定义 Starter 的标准结构 📦
一个规范的自定义 Starter 应该包含两个模块:
xxx-spring-boot-starter(启动器,空jar包,仅做依赖管理)
└── pom.xml(引入xxx-spring-boot-autoconfigure)
xxx-spring-boot-autoconfigure(自动配置核心)
├── src/main/java
│ └── com/xxx/autoconfigure
│ ├── XxxAutoConfiguration.java(自动配置类)
│ └── XxxProperties.java(配置属性绑定类)
└── src/main/resources
└── META-INF
└── spring.factories(注册自动配置类)💡 最佳实践:官方推荐将自动配置和启动器分离,这样其他项目可以直接依赖自动配置模块进行扩展。
官方常用 Starter 速查表 📋
| 分类 | Starter 名称 | 核心功能 |
|---|---|---|
| Web 开发 | spring-boot-starter-web | 构建 Web 应用,包含 Tomcat 和 Spring MVC |
| Web 开发 | spring-boot-starter-webflux | 响应式 Web 开发,基于 Netty |
| 数据访问 | spring-boot-starter-data-jpa | JPA + Hibernate 数据访问 |
| 数据访问 | spring-boot-starter-jdbc | 原生 JDBC + HikariCP 连接池 |
| 数据访问 | spring-boot-starter-data-redis | Redis 操作,默认使用 Lettuce 客户端 |
| 数据访问 | spring-boot-starter-data-mongodb | MongoDB 操作 |
| 消息队列 | spring-boot-starter-amqp | RabbitMQ 消息队列 |
| 消息队列 | spring-boot-starter-kafka | Kafka 消息队列 |
| 安全 | spring-boot-starter-security | Spring Security 安全框架 |
| 测试 | spring-boot-starter-test | 单元测试和集成测试支持 |
| 监控 | spring-boot-starter-actuator | 应用监控和管理端点 |
| 模板引擎 | spring-boot-starter-thymeleaf | Thymeleaf 模板引擎 |
| 工具 | spring-boot-starter-validation | Bean Validation (Hibernate Validator) |
| 工具 | spring-boot-starter-mail | 发送邮件 |
| 工具 | spring-boot-starter-cache | 缓存抽象,配合 Caffeine/Redis |
小贴士:如果你要自定义 Starter,通常起名 xxx-spring-boot-starter,把自己写的自动配置类和依赖打包,其他项目引用后同样享受自动配置。
面试高频追问点 💡
Starter 和 AutoConfiguration 的区别是什么?
Starter是依赖包,只负责引入相关 jarAutoConfiguration是自动配置逻辑,负责创建 Bean- 一个
Starter可以包含多个AutoConfiguration
Spring Boot 如何解决自动配置的冲突问题?
- 使用
@ConditionalOnMissingBean保证用户自定义 Bean 优先 - 使用
@AutoConfigureBefore和@AutoConfigureAfter控制配置顺序 - 可以通过
exclude属性排除不需要的自动配置类
Spring Boot 3.x 对 Starter 机制有什么重要变化?
- 废弃了
spring.factories文件 - 改用
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件 - 每个配置类单独一行,性能更好
如果我听到候选人能说出下面几点,心里基本就亮绿灯了 ✅:
- Starter ≠ 自动配置本身,它是坐标,真正干活的是
autoconfigure模块和条件注解。 - 清楚条件装配的意义,能举出
@ConditionalOnMissingBean允许用户覆盖默认 Bean 的例子。 - 知道新版本用
AutoConfiguration.imports替代spring.factories的演进。 - 实际用过常见的 Starter,并能说上一两个自定义 Starter 的经历就更好了。
Spring Boot 配置文件:application.properties vs application.yml
面试官您好!关于 Spring Boot 中两种主流配置文件的对比,我从语法、功能、使用场景和坑点四个维度给您做个清晰的总结。
最直观的区别嘛,properties是扁平化的key=value,像这样:
server.port=8080
spring.datasource.url=jdbc:mysql://localhost/testyml是树形缩进,看着就清爽很多:
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost/test核心语法对比 📊
| 特性 | application.properties | application.yml |
|---|---|---|
| 语法风格 | 键值对,点分隔 | YAML 缩进式,树形结构 |
| 示例 | server.port=8080 | server:port: 8080 |
| 可读性 | 一般,层级深时混乱 | 优秀,结构清晰直观 |
| 注释 | 仅支持#单行注释 | 支持#单行注释 |
| 空格敏感 | 否 | 是(必须用 2 个空格缩进) |
| 冒号后空格 | 不需要 | 必须有(key: value) |
深层差异
集合和复杂结构的表达力 🧩
properties想写个列表或者Map,得用索引模拟,可读性差:
my.servers[0]=dev.example.com
my.servers[1]=prod.example.com而yml原生支持列表和多层嵌套,一看就懂:
my:
servers:
- dev.example.com
- prod.example.com如果配置里有数组、对象再套数组,yml的优势就炸裂了。
多文档合并能力 📑
Spring Boot会把application.yml和application-{profile}.yml自动合并。而且单个yml文件里还能用---分成多个文档块,配合spring.profiles在一份文件里写多套环境配置,比如:
server:
port: 8080
---
spring:
profiles: dev
server:
port: 8081这比同时维护好几个properties文件要直观不少,改配置不用切窗口。
加载顺序和优先级 ⚖️
当 properties 和 yml 并存时,Spring Boot 默认 properties 优先级高于 yml。这个坑我踩过:有的同事在 yml 里配了端口,结果 properties 里还留着个旧端口,排查半天才发现是 properties “赢”了。所以团队得约定好,别混合用。
深层对比完整图谱 🗺️
| 对比维度 | application.properties | application.yml |
|---|---|---|
| 🎯 语法风格 | 扁平 key=value | 树形 YAML 缩进 |
| 🧠 可读性 | 简单场景还行,层次深就眼花 | 层级分明,对人友好 |
| 🔧 复杂结构 | 只能通过方括号下标模拟 | 原生支持 List、Map、对象嵌套 |
| 📚 多文档 | 每环境一个文件 | 单文件可用---分隔多个文档块 |
| 🚦 优先级 | 更高(同时存在时覆盖 yml) | 较低 |
| ⚡ 上手门槛 | 零门槛 | 注意缩进(不可用 Tab,必须空格) |
功能特性差异 ⚡
1. YAML 独有的优势 ✨
- 支持列表配置:这是 properties 最致命的短板
spring:
profiles:
active: dev, test, prod
datasource:
url: jdbc:mysql://localhost:3306/db
driver-class-name: com.mysql.cj.jdbc.Driver- 支持复杂对象嵌套:配置复杂结构时优势明显
- 支持多文档块:一个文件内可写多个环境配置
---
spring:
profiles: dev
server:
port: 8080
---
spring:
profiles: prod
server:
port: 802. Properties 的优势 👍
- 语法简单:零学习成本,新人上手快
- IDE 支持更成熟:自动补全、提示更完善
- 无格式坑:不用担心缩进错误导致的诡异问题
- 加载优先级略高:同目录下 properties 会覆盖 yml
加载优先级与执行顺序 🚦
重要结论:同位置同名称下,properties 优先级高于 yml,会覆盖 yml 中的相同配置。
🧭 怎么选?给你一个决策图
常见坑点与避坑指南 ⚠️
YAML 的坑 🕳️
- 缩进地狱:必须用 2 个空格,不能用 Tab,否则配置失效
- 特殊字符问题:值包含
:、#、&等必须加引号 - 数字自动转换:
0123会被解析为八进制的83 - 布尔值陷阱:
on/off/yes/no会被自动转为true/false
Properties 的坑 🕳️
- 不支持列表:只能用逗号分隔字符串,代码中手动解析
- 重复前缀多:配置复杂时会出现大量重复前缀
- 层级不清晰:超过 3 层后可读性急剧下降
企业级最佳实践 🏆
- 推荐使用 YAML 作为主配置:结构清晰,功能强大
- 简单配置用 properties:快速原型、临时调试
- 环境隔离方案:
- 主配置:
application.yml(公共配置) - 环境配置:
application-dev.yml、application-prod.yml
- 主配置:
- 敏感信息:不要硬编码,使用环境变量或配置中心
- 格式规范:统一使用 2 个空格缩进,冒号后必须加空格
面试官追问预判 🤔
问:Spring Boot 还支持哪些配置方式?
答:还支持命令行参数、系统属性、环境变量、@PropertySource 注解等,优先级从高到低排列。
问:如何实现配置的动态刷新?
答:Spring Cloud Config 配合 @RefreshScope 注解,或者使用 Nacos/Apollo 等配置中心。
Spring Boot Profile 多环境配置与激活方式
面试官您好,关于 Spring Boot Profile,我从核心作用、配置方式、激活方式和优先级四个方面来回答:
Profile 核心作用 🎯
Profile 是 Spring Boot 提供的环境隔离机制,解决了 "一套代码在不同环境(开发、测试、生产)运行需要不同配置" 的问题。核心就三个点:怎么拆配置、怎么激活、以及优先级。
- 避免硬编码和手动修改配置
- 打包时无需改动代码,只指定环境即可
- 配置清晰分离,便于管理
Profile 配置方式 📝
1. 多配置文件方式(最常用)
命名规则:application-{profile}.yml/properties
| 文件名 | 对应环境 | 说明 |
|---|---|---|
| application.yml | 主配置文件 | 所有环境通用配置 |
| application-dev.yml | 开发环境 | 本地开发用,如本地数据库、debug 日志 |
| application-test.yml | 测试环境 | 测试服务器用,如测试数据库 |
| application-prod.yml | 生产环境 | 线上用,如生产数据库、info 日志 |
📂 结构示例:
src/main/resources
├── application.yml
├── application-dev.yml
├── application-prod.yml
└── application-test.yml🎯 关键点:
application.yml放公共项(如日志格式、公共变量)- 各 profile 文件只放差异项(数据库、redis 地址等),做到最小化覆盖
2. 单文件多文档块方式(YAML 专属)
在一个application.yml中用---分隔不同环境:
# 通用配置
spring:
application:
name: demo-app
---
# 开发环境
spring:
config:
activate:
on-profile: dev
datasource:
url: jdbc:mysql://localhost:3306/dev_db
---
# 生产环境
spring:
config:
activate:
on-profile: prod
datasource:
url: jdbc:mysql://prod-server:3306/prod_db3. 注解方式(针对 Bean)
@Configuration
@Profile("dev") // 只有dev环境才会加载这个配置类
public class DevConfig {
// 开发环境专用Bean
}Profile 激活方式 ⚡(按优先级从低到高排序)
1. 配置文件指定(最低优先级)
在application.yml中设置:
spring:
profiles:
active: dev📊 从低到高的顺序大致如下:
低优先级
↓
default (application.yml)
↓
profile-specific (application-{profile}.yml)
↓
命令行参数
↓
操作系统环境变量
高优先级🏆 记忆口诀:命令行 > 环境变量 > profile 文件 > 默认文件
🌰 举个实际例子:
# application.yml
server:
port: 8080
# application-prod.yml
server:
port: 8443启动命令:
java -jar app.jar --spring.profiles.active=prod --server.port=9090最终生效端口是 9090,因为命令行参数优先级最高 ✨
2. JVM 系统参数
java -Dspring.profiles.active=test -jar app.jar3. 命令行参数(最常用)
java -jar app.jar --spring.profiles.active=prod4. 操作系统环境变量
Linux/Mac:
export SPRING_PROFILES_ACTIVE=prod
java -jar app.jarWindows:
set SPRING_PROFILES_ACTIVE=prod
java -jar app.jar5. Maven 打包时指定
<!-- pom.xml -->
<profiles>
<profile>
<id>prod</id>
<properties>
<spring.profiles.active>prod</spring.profiles.active>
</properties>
</profile>
</profiles>打包命令:
mvn clean package -Pprod进阶骚操作 —— @Profile 注解 + 灵活分组
🧩 注解控制 Bean 加载:
@Configuration
@Profile("dev")
public class DevDataSourceConfig {
// 只在 dev 环境才加载这个数据源
}这样不同环境注入不同实现,比写一堆 if-else 优雅多了。
📦 Profile 分组(Spring Boot 2.4+):
有时候环境要同时激活多个 profile,比如 prod 要连带 cloud 和 mysql 配置:
spring:
profiles:
group:
prod: cloud, mysql
dev: local, h2激活 prod 时自动把 cloud、mysql 拉起来,一组搞定多个切面。
高级特性与注意事项 💡
1.同时激活多个 Profile:用逗号分隔
java -jar app.jar --spring.profiles.active=dev,mysql2.默认 Profile:
如果没有指定任何 Profile,会激活default环境
3.配置覆盖规则:
- 主配置文件(
application.yml)的配置会被特定环境配置覆盖 - 高优先级激活方式会覆盖低优先级的
4.生产环境最佳实践:
- 永远不要在代码中硬编码
spring.profiles.active - 生产环境使用命令行参数或环境变量激活
- 敏感配置(如数据库密码)不要放在配置文件中,使用外部配置或配置中心
5.面试金句总结(就是你能带走的东西)
💡 “命名定配置,参数定激活,注解控加载,分组提效率。”
- 📛 拆:
application-{profile}.yml - 🚀 激:
spring.profiles.active(支持命令行、环境变量、配置文件) - 🧩 注:
@Profile按环境装配 Bean - 📦 组:
spring.profiles.group一次激活多个配置
Spring Boot Actuator 监控端点
面试官您好,Spring Boot Actuator 是 Spring Boot 提供的生产级监控和管理功能,它通过一系列 HTTP 端点暴露应用内部运行状态,是微服务架构中不可或缺的可观测性组件。我分三个层面来讲:最核心的端点、安全暴露策略、生产实战姿势。下面我从几个核心维度来介绍:
核心作用 🔧
- 健康检查:监控应用是否正常运行
- 指标收集:JVM、内存、线程、HTTP 请求等性能指标
- 配置查看:查看应用配置、环境变量、Bean 信息
- 运维操作:优雅停机、日志级别动态调整等
端点分类与常用端点 📊
Actuator 端点分为原生端点和自定义端点,原生端点又可分为三类:
最常用的 10 个端点(面试必问)
| 端点 | 方法 | 核心作用 | 生产环境是否默认开启 | 一句话说人话 |
|---|---|---|---|---|
/actuator/health | GET | 应用健康状态检查,可扩展 HealthIndicator | ✅ 是 | ⚕️ “这服务还喘气吗?” |
/actuator/info | GET | 应用基本信息(自定义),info.* 配置驱动 | ✅ 是 | ℹ️ “你是哪个版本、谁写的?” |
/actuator/metrics | GET | 所有可用指标列表,如 jvm.memory.used | ❌ 否 | 📈 “CPU、内存、请求数全在这” |
/actuator/metrics/{name} | GET | 查看具体指标详情 | ❌ 否 | - |
/actuator/env | GET | 查看环境变量和配置 | ❌ 否 | 🌍 “跑了哪些 profile,数据库地址是啥” |
/actuator/beans | GET | 查看 Spring 容器中所有 Bean | ❌ 否 | 🫘 “Spring 里到底装了哪些豆子” |
/actuator/loggers | GET | 查看所有日志级别 | ❌ 否 | 🪓 “线上不打日志?” |
/actuator/loggers/{name} | POST | 动态修改日志级别 | ❌ 否 | 🪓 “线上不打日志?POST 一下 debug” |
/actuator/shutdown | POST | 优雅关闭应用 | ❌ 否 | - |
/actuator/threaddump | GET | 获取线程 dump 信息 | ❌ 否 | 🧵 “卡住了?抓个线程快照” |
核心配置 ⚙️
1. 依赖引入(Maven)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>2. 关键配置(application.yml)
management:
endpoints:
web:
exposure:
include: health,info,metrics,loggers # 暴露的端点
exclude: shutdown,env,beans # 排除的端点
base-path: /actuator # 端点基础路径
endpoint:
health:
show-details: always # 显示健康详情
shutdown:
enabled: true # 开启优雅停机安全控制 🛡️
生产环境必须做的安全措施:
- 结合 Spring Security 对端点进行权限控制
- 只暴露必要的端点,禁用所有敏感端点
- 修改默认的
/actuator基础路径 - 配置独立的管理端口
management:
server:
port: 9090 # 独立管理端口,与业务端口分离暴露与安全 ⛓️ — 90% 的人会忽略
只背端点没用,暴露策略才决定生死。Spring Boot 的默认规则很简单:
management:
endpoints:
web:
exposure:
include: "health,info" # 默认只放行这两个“无害”端点📌 所有除 health 和 info 之外的端点默认都是关闭的! 你直接访问 /actuator/env 是 404。
常见配置口诀 🧾
- "
*" 暴露所有 web 端点(生产慎用) - "
health,metrics,loggers" 按需开放 - 一定要结合 Spring Security 做角色保护,比如:
management:
endpoint:
health:
show-details: when-authorized结合安全框架,/actuator/** 只允许 ACTUATOR_ADMIN 角色访问。
🚨 血的教训:曾经有公司把 env 全裸暴露到公网,密码、密钥直接被人扒走。
生产用的“三件套” 🛠️
我实际项目里就靠下面三个组合拳:
⚕️ 健康检查的深度定制
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
@Override
public Health health() {
// 模拟检查数据库连接
boolean dbUp = checkDatabase();
if (dbUp) {
return Health.up().withDetail("latency", "10ms").build();
}
return Health.down().withDetail("error", "connection timeout").build();
}
}访问 health 时就能看到自定义细节,配合 K8s 的 livenessProbe 直接决定 Pod 生死。
📈 Prometheus + Micrometer 指标
Spring Boot 2.x 起已内置 Micrometer,加个依赖:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>暴露 /actuator/prometheus,Prometheus 定期来刮,Grafana 面板直接起飞。
🪓 动态日志救火
线上发现某个服务日志太多磁盘要爆,一秒切级别:
curl -实战项目与面试模拟 POST 'http://service/actuator/loggers/com.xxx' \
-H 'Content-Type: application/json' \
-d '{"configuredLevel":"ERROR"}'不用重启,不用发版,爽不爽?
自定义端点 ✨
当原生端点无法满足需求时,可以自定义端点:
@Component
@Endpoint(id = "custom")
public class CustomEndpoint {
@ReadOperation
public Map<String, Object> getCustomInfo() {
Map<String, Object> result = new HashMap<>();
result.put("appName", "我的应用");
result.put("version", "1.0.0");
result.put("timestamp", System.currentTimeMillis());
return result;
}
}访问地址:/actuator/custom 直接返回,还能配上 @WriteOperation 做动态干预。
一张图总结全流程 🌟
面试加分项 💡
- 健康检查的深度:可以自定义
HealthIndicator实现更细粒度的健康检查(如数据库、Redis、MQ 连接状态) - 指标集成:将 Actuator 指标集成到 Prometheus+Grafana 监控体系
- 分布式追踪:结合 Sleuth 和 Zipkin 实现全链路追踪
- 告警机制:基于健康状态和指标阈值配置告警(如邮件、钉钉、企业微信)
“Actuator 是服务的体检报告,但你不能把它贴在大门口让所有人看。” 😂
Spring Boot 异常统一处理:@ControllerAdvice + @ExceptionHandler
面试官:“来,聊聊你们项目中怎么优雅地处理异常?别告诉我还是满屏的 try-catch。” 😏
🎯 核心答案:@ControllerAdvice + @ExceptionHandler
这套组合拳专门解决 “异常满天飞,返回格式各玩各的” 问题,让代码里只有业务逻辑,异常统一拦截、统一包装、统一返回。
核心定位(一句话说清)
解决传统 Controller 层try-catch 满天飞的痛点,实现全局异常统一拦截、统一处理、统一返回,让业务代码更干净,异常逻辑更集中。
核心原理 🧠
@ControllerAdvice:本质是Spring AOP 的全局增强器,专门用于拦截所有 Controller 层的请求和异常@ExceptionHandler:方法级注解,声明该方法能处理哪种类型的异常@RestControllerAdvice:@ControllerAdvice + @ResponseBody的组合注解,前后端分离项目直接用这个就行
异常处理完整流程
标准实现步骤(3 步搞定)
1. 定义统一返回结果类(必须)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
private int code; // 状态码
private String message; // 提示信息
private T data; // 返回数据
// 静态工厂方法,快速构建结果
public static <T> Result<T> success(T data) {
return new Result<>(200, "操作成功", data);
}
public static <T> Result<T> error(int code, String message) {
return new Result<>(code, message, null);
}
}2. 定义全局异常处理器(核心)
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
// 处理自定义业务异常
@ExceptionHandler(BusinessException.class)
public Result<Void> handleBusinessException(BusinessException e) {
log.error("业务异常:{}", e.getMessage(), e);
return Result.error(e.getCode(), e.getMessage());
}
// 处理参数校验异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<Void> handleValidException(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getFieldError().getDefaultMessage();
return Result.error(400, message);
}
// 兜底处理所有其他异常
@ExceptionHandler(Exception.class)
public Result<Void> handleException(Exception e) {
log.error("系统异常:", e);
return Result.error(500, "服务器内部错误,请联系管理员");
}
}3. 自定义业务异常类(强烈推荐)
@Data
public class BusinessException extends RuntimeException {
private int code;
public BusinessException(int code, String message) {
super(message);
this.code = code;
}
}4. Controller 里就剩干净的业务:
@GetMapping("/order/{id}")
public ApiResult<Order> getOrder(@PathVariable Long id) {
Order order = service.findById(id);
if (order == null) {
throw new BizException(404, "订单不存在");
}
return ApiResult.success(order);
}📊 一张图看懂请求怎么被“截胡”的
🔹 DispatcherServlet 是“调度员”,它抓到 Controller 抛出的异常后不直接扔出去,而是先去问 @ControllerAdvice 有没有能处理的方法。
进阶用法(面试加分项 ✨)
- 异常处理优先级:精确匹配 > 父类匹配,局部
@ExceptionHandler> 全局@ExceptionHandler - 处理 404/405 等容器级异常:
# application.yml配置
spring:
mvc:
throw-exception-if-no-handler-found: true
web:
resources:
add-mappings: false- 指定 HTTP 状态码:在异常处理方法上添加
@ResponseStatus(HttpStatus.BAD_REQUEST) - 日志分级记录:业务异常打 warn 日志,系统异常打 error 日志并记录堆栈
- 生产环境隐藏堆栈:通过配置文件控制是否返回详细异常信息
高频踩坑点 ❌
| 坑点 | 原因 | 解决方案 |
|---|---|---|
| 拦截器 / 过滤器里的异常不生效 | @ControllerAdvice只能拦截 Controller 层的异常 | 在过滤器中手动捕获异常并返回 JSON,或使用 ErrorController 兜底 |
| 404 异常始终不被拦截 | Spring 默认将静态资源和 404 请求交给容器处理 | 配置上述两个 yml 参数 |
| 异常匹配混乱 | 多个异常处理方法存在继承关系 | 遵循 "精确匹配优先" 原则,子类异常写在父类前面 |
| 事务回滚失效 | 异常被全局处理器捕获后,事务不会自动回滚 | 确保业务异常是 RuntimeException,或在 @Transactional 中指定 rollbackFor |
与其他异常处理方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
@ControllerAdvice + @ExceptionHandler | 简单易用、代码优雅、灵活度高 | 只能处理 Controller 层异常 | 绝大多数前后端分离项目 |
ErrorController | 能处理所有容器级异常 | 代码繁琐、不够灵活 | 处理 404/500 等兜底异常 |
HandlerExceptionResolver | 最底层、最灵活 | 配置复杂、侵入性强 | 自定义异常处理框架时使用 |
⚙️ 面试官才问的高级细节
❓ 多个 @ControllerAdvice 类,按什么顺序执行?
按 @Order 或 Ordered 接口排序,值越小优先级越高。 没标 @Order 的排最后。同一类内,异常处理器按继承深度匹配(先子类后父类)。
❓ 微服务里返回哪种格式舒服?
用 ApiResult 包装+状态码,前端根据 code 做统一拦截(如 401 跳登录页)。👌
❓ 如何只对指定包生效?
@RestControllerAdvice(basePackages = "com.xxx.order")精准“镇压”订单模块的异常,不误伤别人。
面试终极加分回答
| 老大难问题 | 优雅解决 |
|---|---|
| try-catch 到处都是 | 只抛出自定义异常 |
| 返回格式千奇百怪 | 统一 ApiResult |
| 漏掉的异常导致 500 | Exception.class 兜底 |
| 校验错误处理臃肿 | MethodArgumentNotValidException 统一处理 |
“异常统一处理就是把乱七八糟的错误,用一个漂亮的盒子装好寄回去。” 📦
" 在实际项目中,我会将异常分为业务异常(如参数错误、权限不足)和系统异常(如数据库连接失败)两类。业务异常返回明确的提示信息给前端,系统异常返回通用提示并记录详细日志。同时,我会结合 Sentry 等监控平台自动上报系统异常,方便快速定位问题。"
Spring Boot 3.x 新特性:Jakarta EE 9 迁移、Java 17+ 基线、原生镜像支持
面试官您好,Spring Boot 3.x 是 Spring 生态的里程碑式版本,核心围绕现代化、性能提升和云原生三大方向演进,其中最关键的就是您提到的这三个特性,我来逐一说明:
Java 17+ 基线要求 ☕️
这是最基础也是影响最广的变化,Spring Boot 3.x 彻底放弃了 Java 8 和 Java 11,强制要求最低 Java 17,推荐使用 Java 21 LTS。
核心收益(为什么必须升级?)
| 特性类别 | 具体能力 | 实际开发价值 |
|---|---|---|
| 语言特性 | 记录类 (Record)、模式匹配、密封类 (Sealed)、文本块 | 大幅减少样板代码,代码可读性和可维护性提升 30%+ |
| JVM 性能 | ZGC/Shenandoah 低延迟垃圾回收、AOT 编译、向量 API | 大内存应用停顿时间从秒级降到毫秒级,计算密集型任务提速 20-50% |
| 安全增强 | 更强的模块系统、默认禁用不安全算法 | 从底层提升应用安全性,减少漏洞风险 |
💡 面试加分点:Java 17 是 Oracle 提供的长期支持版本 (LTS),支持到 2029 年,而 Java 11 支持到 2026 年,升级 Java 17 是未来 5 年技术栈的必然选择。
🔥 这几个新特性,你在 Spring Boot 3 里会天天遇到
| 特性 | 你以前写的痛苦代码 | Spring Boot 3 里的优雅写法 |
|---|---|---|
| Record 😎 | DTO 一大堆 @Data/@Getter | record UserDto(String name, int age) {},自动覆盖 equals/hashCode,配置属性 @ConfigurationProperties 也可用 Record |
| Text Block 📜 | 拼接 SQL、JSON 用 + | 用 """ """ 写多行字符串,@Query("""select ...""") 可读性爆表 |
| Sealed Class 🔒 | 状态模式全靠注释约束 | sealed interface Result permits Success, Failure,编译期防扩展,安全策略配置化 |
| Pattern Matching 🧩 | instanceof 后强转 | if (obj instanceof String s && s.length()>5) 直接绑定变量 |
| GC & 性能 ⚡ | CMS 退休,G1 默认,ZGC 改进 | 自动享受 ZGC、Shenandoah 低延迟红利,容器感知内存更准 |
🛡️ 兼容性防火墙
升级时记得检查:
- Groovy、Kotlin、Lombok 版本要对齐 Java 17 class 文件。
- 字节码操作类库(ASM、CGLIB)必须升级,否则类增强直接炸。
- Spring Boot 3 最低要求 Java 17,Java 8/11 直接启动失败——硬约束。
Jakarta EE 9 全面迁移 📦
这是 Spring Boot 3.x 最具破坏性的变化,核心是所有 javax. 包名全部改为 jakarta.*。* 本质是 Java EE 捐献给了 Eclipse 基金会后不得不改名。
迁移前后包名对比
关键影响与注意事项
- 依赖升级:所有基于 Java EE 的第三方库必须升级到支持 Jakarta EE 9+ 的版本(如 MyBatis 3.5.10+、Druid 1.2.16+)
- 代码修改:全局替换
import javax.*为import jakarta.*(IDE 可一键完成) - 兼容性问题:部分老旧库(如 EJB、JAX-RS 旧实现)不再支持,需要寻找替代方案
⚠️ 踩坑提醒:如果项目中使用了大量第三方依赖,一定要先检查它们是否有 Jakarta EE 兼容版本,否则会出现 ClassNotFoundException。
✅ 面试亮点:你说“我们用 OpenRewrite 的 javax-to-jakarta 脚本自动迁移了整个项目,5 千多个文件改完跑集成测试一把过。” —— 面试官立马加 10 分。
原生镜像支持(Spring Native) 🚀
这是 Spring Boot 3.x 最亮眼的特性,通过集成 GraalVM 实现提前编译 (AOT),将 Java 应用编译成原生可执行文件。
🧠 AOT + GraalVM 的本质
不再是 Spring Native 实验项目,Spring Boot 3 直出 AOT(Ahead-Of-Time)处理引擎,把运行时的反射、代理、序列化等 提前编译 成静态配置,喂给 GraalVM 生成原生可执行文件。
📊 一张图看懂差异
传统 JVM 启动流程:
JAR → JVM 加载 → 类扫描 → 条件评估 → 代理生成 → 启动完成
时间:2~3 秒 😴
原生镜像启动流程:
AOT 编译期 → 已固化 Bean 定义、代理类、反射配置
→ 原生可执行文件 → 直接运行
时间:0.05~0.1 秒 🏎️💨传统 JVM vs 原生镜像性能对比
核心优势
- 极速启动:启动时间从秒级降到毫秒级,完美适配 Serverless 和微服务场景
- 超低内存:内存占用减少 80% 以上,大幅降低云服务器成本
- 无依赖运行:生成的原生镜像不需要安装 JVM,直接在操作系统上运行
局限性
- 编译时间长:大型项目编译原生镜像可能需要几分钟
- 动态特性受限:反射、动态代理、资源加载等需要显式配置
- 调试困难:原生镜像的调试体验不如传统 JVM
✨ 实战建议:原生镜像特别适合无状态微服务、定时任务、CLI 工具等场景,对于需要大量动态特性的复杂应用,建议先评估再使用。
🛠️ 你该怎么玩?
- 引入 GraalVM Native Build Tools,
mvn -Pnative native:compile。 - 配合 Hints 注册动态行为(
@RegisterReflectionForBinding、RuntimeHintsRegistrar)。 - 有些东西不能原生就退回到常规 JVM 模式,比如运行时定义太多的动态脚本引擎。
- 云原生场景尤其适合:K8s 里的 Pod 从启动到 Ready 只需眨眼,缩扩容弹性吊打 JVM 模式。
✅ 面试亮点:“我们之前微服务冷启动 3 秒导致 K8s 探活经常超时,切原生镜像后直接压缩到 80 毫秒,服务注册瞬间完成,灰度发布风险低了很多。”
🧩 三者联动全景图
Spring Boot 与 Spring 核心区别
一句话核心总结 ✨
Spring 是一个全面的 Java 企业级开发框架,提供了 IOC、AOP 等核心能力;而 Spring Boot 是基于 Spring 的 "快速开发脚手架",它不是替代 Spring,而是让 Spring 用起来更简单、更高效。
举个例子 🧩
Spring是“毛坯房”,Spring Boot是“精装房” 🏠
Spring 给了你无限可能,但需要你自己砌墙、走水电;
Spring Boot 则已经把硬装软装都搞定了,你拎包入住,专注于你的业务家具摆设。
核心区别对比表 📊
| 对比维度 | Spring Framework | Spring Boot |
|---|---|---|
| 核心理念 | 提供企业级开发的全套解决方案 | 约定大于配置 (Convention over Configuration) |
| 配置方式 | 大量 XML/Java 配置,手动配置 Bean | 自动配置 (Auto-Configuration),默认配置满足 80% 场景 |
| 依赖管理 | 手动引入所有依赖,极易出现版本冲突 | Starter 启动器,自动管理依赖版本,"一键引入" |
| 部署方式 | 打包为 WAR,部署到外部 Tomcat 等容器 | 内置 Tomcat/Jetty/Undertow,直接运行 JAR 包 |
| 开发效率 | 样板代码多,配置繁琐,上手门槛高 | 零配置启动,快速开发,几分钟就能跑通一个项目 |
| 生态整合 | 需要手动整合第三方技术,配置复杂 | 提供上百个官方 Starter,开箱即用整合各类技术 |
| 监控能力 | 无内置监控,需要额外集成 | Actuator 组件,提供健康检查、指标监控等开箱即用能力 |
| 版本仲裁 | 开发者自己解决依赖冲突,经常 “jar 地狱” 😈 | 有 spring-boot-dependencies BOM 统一管理,不用操心版本 |
🔹 面试金句:Spring Boot 的 starter 是一组预置的描述符(pom),把某个场景下需要的依赖直接集成好,避免版本冲突,极大提升开发效率。
🎯 核心区别一图胜千言
配置方式 —— 从“长篇古文”到“智能填表”
- Spring:经典的
applicationContext.xml、dispatcher-servlet.xml……一堆 XML 配置,bean 一个个定义,AOP、事务配置繁琐得让人头大 😓 - Spring Boot:约定大于配置!默认配置覆盖 80% 场景,你需要改的话,一个
application.yml或application.properties里改几个 key 就搞定;甚至可以通过自动配置(@EnableAutoConfiguration)按条件装配 Bean。
# Spring Boot 连接MySQL示例
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: 123456
jpa:
hibernate:
ddl-auto: update以前 Spring 要写多少 XML?现在三行 YAML 搞定 🔥
🔹 面试金句:Spring Boot 的 @SpringBootApplication 等于 @Configuration + @EnableAutoConfiguration + @ComponentScan,自动扫描并配置。
部署运行 —— 从“找宾馆”到“胶囊公寓”
- Spring:需要打 war 包,外置 Tomcat/Jetty 等服务器,还得配置一堆服务器参数。
- Spring Boot:内嵌 Tomcat、Jetty、Undertow,直接打成一个可执行 fat jar,
java -jar一条命令跑起来。微服务、容器化部署友好度满分 💯
# Spring Boot 启动就是这么简单
java -jar myapp.jar以前要装 Tomcat,现在连服务器都内置,像随身带着个胶囊旅馆 🚅
🩺 生产就绪特性 —— 从“裸奔”到“带健康手环”
| 功能 | Spring 框架 | Spring Boot |
|---|---|---|
| 健康检查 | 需要自己集成或写端点 | spring-boot-actuator 暴露 /health, /info 等端点,即配即用 |
| 指标监控 | 手动整合 Micrometer 等 | 同样 Actuator 自动整合,Prometheus 等轻松对接 |
| 外部化配置 | 可用 @Value,但整体费劲 | 完美支持多环境 profile,配置中心轻松对接 |
面试官如果追问 “你怎么监控线上服务?”,你可以淡定地说: “我们用 Spring Boot Actuator 暴露健康检查、metrics 指标,结合 Prometheus + Grafana 做监控大盘。” 立刻显得靠谱 👍
🧠 底层原理小灶(加分项)
- Spring 核心:IoC 容器(BeanFactory / ApplicationContext)和 AOP,提供基础的 Bean 管理、依赖注入、面向切面编程。
- Spring Boot 魔法:基于
@Conditional注解的条件装配。比如@ConditionalOnClass(类路径下有某个类才生效)、@ConditionalOnMissingBean(你没手动声明 Bean 时才会自动配置)。通过spring.factories里的EnableAutoConfiguration条目,加载所有自动配置类,按需激活。
说白了,Spring Boot 就是提前写好了一大堆“如果…那么…”的自动化配置类,你引入哪个 starter,就激活对应的条件,省去你手工折腾 🧙✨
最直观的开发流程对比 🚀
📉 最后一张对比表,给你清清楚楚
📋 Spring vs Spring Boot 核心差异速览
┌──────────────────┬──────────────────────────────┬──────────────────────────────┐
│ 维度 │ Spring 框架 │ Spring Boot │
├──────────────────┼──────────────────────────────┼──────────────────────────────┤
│ 设计目标 │ 企业级应用开发的“乐高积木” │ 快速构建独立、生产级的 Spring 应用│
│ 依赖管理 │ 手动,易冲突 │ starter 集成,BOM 统一管理 │
│ 配置方式 │ XML/注解,工作量大 │ 约定>配置,yaml/properties 简化 │
│ 部署方式 │ 需外置 Servlet 容器 │ 内嵌容器,jar 包直接运行 │
│ 运维监控 │ 需手工整合 │ Actuator 开箱即用 │
│ 学习曲线 │ 陡峭,需要懂很多底层 │ 平滑,新人半天上手 │
│ 适合场景 │ 需要极度灵活的大型项目 │ 微服务、云原生、快速迭代项目 │
└──────────────────┴──────────────────────────────┴──────────────────────────────┘面试官最想听的 3 个关键点 💡
- 自动配置原理:Spring Boot 通过
@EnableAutoConfiguration注解,扫描META-INF/spring.factories文件中的自动配置类,根据条件注解 (@ConditionalOnClass、@ConditionalOnMissingBean等) 自动装配 Bean,这是 Spring Boot 的灵魂。 - Starter 的本质:Starter 不是什么神奇的东西,它只是一个 "依赖描述符",帮你把某个功能需要的所有依赖都打包好,并且保证版本兼容。比如
spring-boot-starter-web就包含了 Spring MVC、Tomcat、Jackson 等所有 Web 开发需要的依赖。 - 两者的关系:Spring Boot 没有重写 Spring 的任何核心功能,它只是在 Spring 的基础上做了一层封装和自动化。所有 Spring 能做的事情,Spring Boot 都能做,而且做得更简单。
