跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
Javajava

Spring Boot 核心原理与面试高频考点解析

Spring Boot 自动配置机制基于 SPI 和条件注解,启动流程涉及 SpringApplication 生命周期及内嵌容器初始化。内容涵盖 Starter 定制、配置加载顺序、多数据源切换、Actuator 安全、异步任务处理及 JVM 内存排查等核心考点。针对 Spring Boot 3.x 的 Jakarta EE 迁移、GraalVM 支持及优雅停机策略也做了详细梳理,适合中高级 Java 工程师深入理解框架底层原理。

深海蔚蓝发布于 2026/3/280 浏览
Spring Boot 核心原理与面试高频考点解析

Spring Boot 自动配置机制

1. 自动配置是如何工作的?

Spring Boot 自动配置的核心在于 SPI 机制与条件注解的配合。启动时,@SpringBootApplication 包含 @EnableAutoConfiguration,它会扫描所有 JAR 包中的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(2.7+)或旧版的 spring.factories。

读取到 EnableAutoConfiguration 对应的全限定类名后,Spring 容器会评估每个配置类上的 @ConditionalOn... 注解(如 @ConditionalOnClass)。只有满足条件的配置类才会生效,并通过 @Bean 方法向容器注册组件。这里有个关键点:用户自定义的 Bean 优先于自动配置,因为自动配置通常带有 @ConditionalOnMissingBean。如果需要控制顺序,可以使用 @AutoConfigureBefore/After。

Spring Boot 启动流程

2. 完整启动流程是怎样的?

从 main() 到 Web 容器启动,关键步骤如下:

  1. 执行 SpringApplication.run():创建实例并推断应用类型(SERVLET/REACTIVE/NONE)。
  2. 初始化监听器和初始器:加载 spring.factories 中注册的 ApplicationContextInitializer 和 ApplicationListener。
  3. 创建 ApplicationContext:根据类型创建 AnnotationConfigServletWebServerApplicationContext。
  4. 准备上下文:应用初始器,注册主配置类。
  5. 刷新上下文:调用 AbstractApplicationContext.refresh()。这是核心生命周期,包括执行 BeanFactoryPostProcessor、扫描注册 BeanDefinition、实例化非懒加载单例 Bean。
  6. 内嵌 Web 容器启动:ServletWebServerFactoryAutoConfiguration 生效,创建工厂并在 refresh 末尾启动 Tomcat。
  7. 发布事件:最后发布 ApplicationStartedEvent 和 ApplicationReadyEvent。

Starter 定制与扩展

3. 如何自定义 Starter?

Starter 结构通常分为依赖管理模块和自动配置逻辑模块。设计时要遵循分离原则,starter 只声明依赖,autoconfigure 模块包含实际逻辑。

在自动配置类中,务必使用条件注解来避免冲突。例如:

@Configuration
@ConditionalOnClass(MyService.class)
@ConditionalOnMissingBean(MyService.class)
public class MyServiceAutoConfiguration {
    @Bean
    public MyService myService() {
        return new MyService();
    }
}

同时提供配置属性绑定,通过 @ConfigurationProperties("my.service") 注入。最后记得将配置类注册到 org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中。

配置管理与动态刷新

4. 外部化配置加载顺序与动态刷新?

加载优先级从高到低依次为:命令行参数 > 环境变量 > application.properties > 默认属性等。注意 application.properties 在不同路径下的优先级不同。

关于动态刷新,推荐方案是结合 Spring Cloud Config 和 @RefreshScope。在需要刷新的 Bean 上加注解,调用 /actuator/refresh 即可。如果不想引入 Spring Cloud,也可以监听 EnvironmentChangeEvent 手动更新内部状态,但普通 @ConfigurationProperties Bean 不会自动刷新,需配合 Scope 使用。

Web 容器可插拔性

5. 如何实现内嵌 Web 容器的可插拔?

Spring Boot 通过 ServletWebServerFactory 接口抽象了 Web 容器。自动配置类会根据 Classpath 条件判断是否启用 Tomcat、Jetty 或 Undertow。

若要切换为 Undertow,只需排除 Tomcat 依赖并引入 Undertow Starter:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

Actuator 安全与健康检查

6. Actuator 端点安全与自定义健康检查?

默认只暴露 /health 和 /info。生产环境若开放所有端点,必须集成 Spring Security 进行权限控制。可以配置 management.endpoints.web.exposure.include 限制访问范围。

自定义健康检查很简单,实现 HealthIndicator 接口即可。例如检查外部服务状态,返回 Health.up() 或 Health.down()。

异步任务处理

7. 正确使用 @Async 与线程池配置?

开启异步需在启动类加 @EnableAsync。注意不能在同一个类中调用 @Async 方法,因为基于代理的内部调用会绕过代理,导致不异步执行。

建议自定义线程池,避免使用默认的 SimpleAsyncTaskExecutor。配置核心线程数、队列容量及拒绝策略,并设置线程名前缀以便排查问题。

内存泄漏排查

8. 常见内存泄漏原因与优化?

常见原因包括静态集合持有引用、未关闭资源、ThreadLocal 未清理等。排查工具推荐使用 jstat、jmap 配合 MAT 分析堆转储文件。

优化方面,缓存建议使用 WeakHashMap 或带 TTL 的 Caffeine;资源操作尽量用 try-with-resources;ThreadLocal 使用后务必调用 remove()。

多数据源切换

9. 如何实现动态数据源切换?

静态多数据源定义多个配置类并用 @Primary 指定默认。动态切换则基于 AOP 实现。

思路是自定义注解标记方法,通过 AOP 切面获取注解值,存入 ThreadLocal,然后继承 AbstractRoutingDataSource 重写 determineCurrentLookupKey 方法从 ThreadLocal 获取当前数据源。这样就能在事务上下文中灵活切换。

Spring Boot 3.x 迁移

10. 主要变化与 GraalVM 支持?

Spring Boot 3 最低要求 JDK 17,包名从 javax.* 变为 jakarta.*。这带来了兼容性问题,第三方库需升级。

GraalVM Native Image 支持让应用编译为原生可执行文件,启动快且内存占用低。但需注意反射和动态代理需显式配置 @NativeHint。

配置类代理机制

11. @Configuration 为何是 CGLIB 代理?

Spring 默认对 @Configuration 类启用 CGLIB 代理,目的是保证 @Bean 方法的单例语义。如果没有代理,类内部调用 @Bean 方法会创建新实例,破坏单例。有了代理,调用会被拦截并从容器中获取已注册的 Bean。

启动性能优化

12. 启动慢的原因与诊断?

常见原因有自动配置过多、组件扫描范围大、数据库连接初始化慢等。诊断可开启 StartupInfoLogger 或使用 Startup Metrics。

优化手段包括排除不必要的自动配置、缩小扫描范围、开启延迟初始化,或者在 Spring Boot 3 中使用 GraalVM Native Image 实现毫秒级启动。

优雅停机

13. 如何实现优雅停机?

Spring Boot 2.3+ 支持配置 server.shutdown: graceful。收到 SIGTERM 信号后,会停止接收新请求,等待正在处理的请求完成,再依次关闭容器和上下文。

注意陷阱:长任务阻塞会导致无法中断,异步任务可能未完成。K8s 环境下应配合就绪探针,在停机前将 Pod 标记为 NotReady。

初始化时机区别

14. Initializer 与 Runner 的区别?

ApplicationContextInitializer 用于在 Context 刷新前修改配置,适合 Vault 注入或 K8s 绑定。

CommandLineRunner / ApplicationRunner 则在 Context 刷新完成后执行,适合启动时做 DB migration 或加载字典表。两者层级不同,用途分明。

循环依赖处理

15. 如何防止循环依赖?

Spring 依然依赖三级缓存解决循环依赖,但构造器注入无法解决,会直接报错。字段或 Setter 注入虽能解决,但属于'带伤运行'。最佳实践是通过重构消除循环依赖,而非依赖框架兜底。

健康检查聚合

16. Health 端点如何聚合?

默认只要有一个指示器 DOWN,整体就是 DOWN。可以通过实现 ManagementHealthAggregator 自定义聚合策略。例如,只有核心服务 DOWN 才标记整体 DOWN,其他非核心服务失败不影响 UP 状态。

分布式事务选型

17. 如何处理分布式事务?

Seata 适用于金融核心系统(AT/TCC),电商下单可用 Saga 模式,日志通知类可用消息队列最终一致。选型依据是业务对一致性的要求和性能权衡。

K8s 探针设计

18. 存活与就绪探针如何设计?

Liveness Probe 判断应用是否活着,失败重启 Pod,不应包含外部依赖以免误杀。

Readiness Probe 判断是否准备好接收流量,失败移除 Endpoint,可包含 DB、Redis 检查。Spring Boot 2.3+ 内置支持 livenessState 和 readinessState。

JSON 序列化安全

19. 防止无限递归与敏感泄露?

实体类双向关联可能导致栈溢出,推荐使用 DTO 转换或 @JsonManagedReference。敏感信息泄露可通过 @JsonIgnore 或 @JsonView 控制返回字段。全局配置 Jackson 时也可禁用日期时间戳等功能。

Jakarta EE 迁移

20. 兼容性问题与平滑升级?

主要变化是包名替换。兼容性问题主要来自未升级的第三方库。平滑升级策略是先升级到 Spring Boot 2.7 修复警告,再升级 3.x 并批量替换 import,最后验证依赖树兼容性。

目录

  1. Spring Boot 自动配置机制
  2. Spring Boot 启动流程
  3. Starter 定制与扩展
  4. 配置管理与动态刷新
  5. Web 容器可插拔性
  6. Actuator 安全与健康检查
  7. 异步任务处理
  8. 内存泄漏排查
  9. 多数据源切换
  10. Spring Boot 3.x 迁移
  11. 配置类代理机制
  12. 启动性能优化
  13. 优雅停机
  14. 初始化时机区别
  15. 循环依赖处理
  16. 健康检查聚合
  17. 分布式事务选型
  18. K8s 探针设计
  19. JSON 序列化安全
  20. Jakarta EE 迁移
  • 💰 8折买阿里云服务器限时8折了解详情
  • 💰 8折买阿里云服务器限时8折购买
  • 🦞 5分钟部署阿里云小龙虾了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog

更多推荐文章

查看全部
  • C++ STL list 容器详解:使用与模拟实现
  • OpenClaw 开源 AI 智能体框架技术解析与部署实践
  • OpenClaw 部署指南:Minimax/DeepSeek 模型与飞书机器人集成
  • MySQL 5.7 解压版安装与配置实战
  • MySQL 数据库基础入门:从概念到实战
  • MySQL 索引原理:B+ 树结构与实战优化
  • C++ 多态详解:从实现条件到底层原理
  • 位运算算法实战:6 道经典题目详解(字符唯一性、缺失数字等)
  • NC221681 dd 爱框框:滑动窗口算法实战
  • KingbaseES 内核级 SQL 防火墙:白名单机制与零误报实践
  • Flash 存储磨损均衡算法原理与实现
  • OpenClaw 汉化版部署指南:npm/Docker/脚本三种安装方式详解
  • PowerShell Invoke-WebRequest 报错 Invalid URL 和 CommandNotFound 排查指南
  • C++ 模板机制与 String 类深度解析
  • OpenClaw 部署与飞书机器人接入指南
  • C++ 哈希表封装:模拟实现 unordered_map 与 unordered_set
  • 无人机视角山区泥石流与滑坡图像识别数据集
  • Python 中 == 与 is 操作符的本质区别与最佳实践
  • 无人机航测正射影像制作:ContextCapture 与 Pix4D 实战指南
  • RTD1296PB 与 RK3568:NAS 与智能家居芯片实战对比

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online