跳到主要内容
Spring Boot 核心原理与面试高频考点解析 | 极客日志
Java java
Spring Boot 核心原理与面试高频考点解析 Spring Boot 自动配置基于条件注解与 SPI 机制实现 Bean 注册,启动流程涵盖上下文创建、刷新及 Web 容器初始化。Starter 设计需分离依赖与配置,利用 @ConditionalOnMissingBean 避免冲突。外部化配置优先级明确,动态刷新可结合 Actuator 或 RefreshScope。内嵌容器支持 Tomcat/Jetty/Undertow 切换,Actuator 端点需配合 Security 保障安全。异步任务需注意代理机制与线程池配置,内存泄漏排查依赖 JVM 工具。多数据源通过 AOP 路由实现,GraalVM 原生镜像优化启动性能。优雅停机处理 SIGTERM 信号,三级缓存解决循环依赖。Kubernetes 探针设计区分存活与就绪状态,序列化防递归与敏感泄露,Jakarta EE 迁移需处理包名变更。
AiEngineer 发布于 2026/3/26 更新于 2026/5/10 28 浏览Spring Boot 核心原理与面试高频考点解析
1. Spring Boot 的自动配置是如何工作的?
答案:
Spring Boot 自动配置的核心流程如下:
入口 :@SpringBootApplication 包含 @EnableAutoConfiguration。
加载候选配置类 :
Spring Boot 启动时扫描所有 JAR 包中的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(Spring Boot 2.7+)或旧版的 META-INF/spring.factories。
读取其中 org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的全限定类名列表。
条件过滤 :
每个自动配置类(如 DataSourceAutoConfiguration)都带有 @ConditionalOn… 注解(如 @ConditionalOnClass, @ConditionalOnMissingBean)。
Spring 容器在注册 Bean 前会评估这些条件,只有满足条件的配置类才会生效。
Bean 注册 :
自动配置类通过 @Configuration + @Bean 方法向容器注册组件(如 DataSource, JdbcTemplate)。
优先级控制 :
用户自定义的 Bean 优先于自动配置(因 @ConditionalOnMissingBean)。
可通过 @AutoConfigureBefore/After 控制自动配置类之间的顺序。
考察点 :SPI 机制、条件装配、启动流程、扩展点设计。
2. Spring Boot 应用的完整启动流程是怎样的?
答案:
Web 应用关键步骤如下:
执行 SpringApplication.run() :
创建 SpringApplication 实例,推断应用类型(SERVLET/REACTIVE/NONE)。
初始化监听器和初始器 :
加载 spring.factories 中的 ApplicationContextInitializer 和 ApplicationListener。
创建 ApplicationContext :
根据应用类型创建 AnnotationConfigServletWebServerApplicationContext。
准备上下文(prepareContext) :
应用 ApplicationContextInitializer。
注册主配置类(即带 @SpringBootApplication 的类)。
刷新上下文(refresh) :
调用 AbstractApplicationContext.refresh(),这是 Spring 的核心生命周期方法。
执行 BeanFactoryPostProcessor(包括 ConfigurationClassPostProcessor 解析 )。
@Configuration
扫描并注册 BeanDefinition。
实例化非懒加载的单例 Bean。 内嵌 Web 容器启动 :
ServletWebServerFactoryAutoConfiguration 生效,创建 TomcatServletWebServerFactory。
在 refresh 阶段末尾,调用 onRefresh() → createWebServer() → 启动 Tomcat。
发布 ApplicationStartedEvent / ApplicationReadyEvent 。考察点 :启动生命周期、上下文刷新、内嵌容器集成、事件机制。
3. 如何自定义 Starter?
my-spring-boot-starter(依赖管理)
my-spring-boot-autoconfigure(自动配置逻辑)
分离依赖与配置 :starter 只声明依赖,autoconfigure 模块包含实际逻辑。
使用条件注解 :
@ConditionalOnClass(MyService.class)
@ConditionalOnMissingBean(MyService.class)
@Configuration
public class MyServiceAutoConfiguration {
@Bean
public MyService myService () {
return new MyService ();
}
}
@ConfigurationProperties("my.service")
public class MyServiceProperties {
}
并在自动配置类中注入。
4. 注册到 spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports :
com.example.MyServiceAutoConfiguration
始终使用 @ConditionalOnMissingBean。
属性前缀命名规范(如 my.starter.xxx)。
提供默认值但允许完全关闭(如 enabled 开关)。
4. Spring Boot 中的外部化配置加载顺序是什么?
命令行参数(–server.port=8081)
SPRING_APPLICATION_JSON
ServletConfig / ServletContext 初始化参数
JNDI 属性
Java System Properties (System.getProperties())
OS 环境变量
application.properties(当前目录/config → 当前目录 → classpath:/config/ → classpath:/)
@PropertySource 注解
默认属性(SpringApplication.setDefaultProperties)
方案 1(推荐) :使用 Spring Cloud Config + @RefreshScope
添加 spring-boot-starter-actuator 和 spring-cloud-starter-config
在需要刷新的 Bean 上加 @RefreshScope
调用 /actuator/refresh 触发刷新
方案 2 :监听 EnvironmentChangeEvent
@EventListener
public void onEnvironmentChange (EnvironmentChangeEvent event) {
if (event.getKeys().contains("my.config.key" )) {
}
}
注意:普通 @ConfigurationProperties Bean 不会自动刷新,需配合 @RefreshScope。
5. Spring Boot 如何实现内嵌 Web 容器的可插拔?
Spring Boot 通过 ServletWebServerFactory 接口抽象 Web 容器。
自动配置类 TomcatServletWebServerFactoryConfiguration、JettyServletWebServerFactoryConfiguration 等根据 classpath 条件生效。
例如:若 org.eclipse.jetty.server.Server 在 classpath 且 org.apache.catalina.startup.Tomcat 不在,则启用 Jetty。
<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 >
6. Spring Boot Actuator 的端点安全如何保障?
默认只暴露 /health 和 /info(Web 环境)。
通过 management.endpoints.web.exposure.include=* 开放所有端点(生产环境必须配合安全)。
必须集成 Spring Security:
management:
endpoints:
web:
exposure:
include: health,metrics,env
endpoint:
health:
show-details: always
@Configuration
public class ActuatorSecurity extends WebSecurityConfigurerAdapter {
@Override
protected void configure (HttpSecurity http) throws Exception {
http.requestMatcher(EndpointRequest.toAnyEndpoint())
.authorizeRequests()
.anyRequest().hasRole("ACTUATOR" )
.and().httpBasic();
}
}
@Component
public class CustomHealthIndicator implements HealthIndicator {
@Override
public Health health () {
boolean isOk = checkExternalService();
if (isOk) {
return Health.up().withDetail("service" ,"available" ).build();
} else {
return Health.down().withDetail("reason" ,"timeout" ).build();
}
}
}
7. Spring Boot 中如何正确处理异步任务?
在启动类或配置类上加 @EnableAsync。
在 Service 方法上加 @Async。
不能在同一个类中调用:因为 @Async 基于代理(JDK/CGLIB),内部调用绕过代理,不会异步执行。
自定义线程池(避免使用默认 SimpleAsyncTaskExecutor):
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean("taskExecutor")
public Executor taskExecutor () {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor ();
executor.setCorePoolSize(5 );
executor.setMaxPoolSize(10 );
executor.setQueueCapacity(100 );
executor.setThreadNamePrefix("async-task-" );
executor.setRejectedExecutionHandler(new ThreadPoolExecutor .CallerRunsPolicy());
executor.initialize();
return executor;
}
}
实现 AsyncUncaughtExceptionHandler 捕获未处理异常。
或返回 CompletableFuture 显式处理异常。
8. Spring Boot 应用内存泄漏的常见原因有哪些?
静态集合持有 Bean 引用:如 static Map 缓存未清理。
未关闭资源:数据库连接、文件流、HTTP 客户端未 close。
ThreadLocal 未清理:尤其在线程池中复用线程。
Actuator 端点缓存:如 /heapdump 生成大文件。
CGLIB 代理类过多:动态生成类占用 Metaspace。
jstat -gc:观察 GC 频率和堆内存。
jmap -histo:live:查看对象分布。
jmap -dump:format=b,file=heap.hprof + MAT 分析。
Spring Boot Actuator 的 /metrics/jvm.memory.used。
使用 WeakHashMap 或显式 TTL 缓存(如 Caffeine)。
资源使用 try-with-resources。
ThreadLocal 使用后调用 remove()。
限制线程池大小和队列容量。
9. Spring Boot 如何支持多数据源?
定义多个 @Configuration,分别创建 DataSource、SqlSessionFactory、TransactionManager。
使用 @Primary 指定默认数据源。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DS {
String value () ;
}
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey () {
return DataSourceContextHolder.get();
}
}
@Aspect
@Component
public class DataSourceAspect {
@Before("@annotation(ds)")
public void switchDS (DS ds) {
DataSourceContextHolder.set(ds.value());
}
@After("@annotation(ds)")
public void restoreDS () {
DataSourceContextHolder.clear();
}
}
@Bean
@Primary
public DataSource dynamicDataSource () {
DynamicDataSource ds = new DynamicDataSource ();
ds.setTargetDataSources(Map.of("master" , masterDS, "slave" , slaveDS));
ds.setDefaultTargetDataSource(masterDS);
return ds;
}
10. Spring Boot 3.x 迁移的主要变化有哪些?
最低 JDK 版本要求 JDK 17 :
利用新语言特性(如 Records、Pattern Matching)。
与 Jakarta EE 9+ 对齐(包名从 javax.* → jakarta.*)。
GraalVM Native Image 支持 :
通过 spring-native 项目(已合并到 Spring Boot 3)。
编译为 native 可执行文件,启动快(毫秒级)、内存低(~50MB)。
限制:反射、动态代理需显式配置(@NativeHint)。
移除过时 API :
如 WebMvcConfigurerAdapter(Java 8 接口默认方法已足够)。
Micrometer 2.0 集成:统一指标观测,替代旧版 Metrics。
11. Spring Boot 中 @Configuration 类为何默认是 CGLIB 代理?
Spring Boot 默认对 @Configuration 类启用 CGLIB 代理(即使没有 @EnableAspectJAutoProxy),这是由 ConfigurationClassPostProcessor 在解析阶段决定的。
目的:实现 @Bean 方法的单例语义保证(即多次调用同一 @Bean 方法返回同一个实例)。
@Configuration
public class AppConfig {
@Bean
public ServiceA serviceA () {
return new ServiceA ();
}
@Bean
public ServiceB serviceB () {
return new ServiceB (serviceA());
}
}
如果没有代理,serviceB() 中调用 serviceA() 会创建新实例,破坏单例。
有了 CGLIB 代理,serviceA() 调用会被拦截,转而从 Spring 容器中获取已注册的单例 Bean。
考察点 :Spring 容器生命周期、代理机制、配置类语义保障。
12. Spring Boot 应用启动慢的常见原因有哪些?
自动配置类过多:大量 @ConditionalOn… 条件判断耗时。
组件扫描范围过大:@ComponentScan 扫描了不必要的包。
数据库连接初始化慢:HikariCP 获取连接超时或 DNS 解析慢。
第三方 SDK 初始化:如 Redis、Kafka 客户端建立连接。
类加载慢:JAR 包过多、Fat Jar 解压开销大。
logging.level.org.springframework.boot.StartupInfoLogger=DEBUG
使用 Spring Boot 2.7+ 的 Startup Metrics :
management:
metrics:
enable: true
tags:
application: ${spring.application.name}
查看 /actuator/metrics/startup.*。
3. 使用 Async Profiler 或 JFR (Java Flight Recorder) 分析 CPU/锁/IO。
排除不必要的自动配置:@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
缩小 @ComponentScan 范围。
延迟初始化(Spring Boot 2.2+):
spring.main.lazy-initialization=true
使用 GraalVM Native Image(Spring Boot 3+)实现毫秒级启动。
13. Spring Boot 如何实现优雅停机? server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
收到 SIGTERM 信号后,Spring Boot:
停止接收新请求(Tomcat/Jetty 拒绝新连接)。
等待正在处理的请求完成(最长 timeout-per-shutdown-phase)。
依次关闭:Web 容器 → 执行 DisposableBean.destroy() → 关闭 ApplicationContext。
利用 Spring 的 Lifecycle 和 SmartLifecycle 接口协调关闭顺序。
长任务阻塞:若业务线程执行长时间任务(如 while(true)),不会被中断,需自行监听 ContextClosedEvent。
异步任务未完成:@Async 任务可能被强制终止,建议使用 TaskExecutor 的 awaitTermination。
K8s 就绪探针:应配合 readinessProbe 在停机前将 Pod 标记为 NotReady,避免流量打入。
14. ApplicationContextInitializer 和 Runner 的区别?
ApplicationContextInitializer:Vault 配置注入、K8s ConfigMap 动态绑定。
CommandLineRunner:启动时执行 DB migration、加载字典表到内存。
考察点 :扩展点时机、初始化逻辑分层、安全敏感操作位置。
15. Spring Boot 如何防止'循环依赖'? Spring Boot 并未改变 Spring Framework 的循环依赖处理机制,依然依赖三级缓存:
singletonObjects:成品单例池
earlySingletonObjects:早期暴露的 Bean(尚未完成属性注入)
singletonFactories:ObjectFactory,用于生成早期引用
构造器注入(@Autowired on constructor)无法解决循环依赖,会直接报错(推荐做法,避免设计缺陷)。
字段/Setter 注入可被三级缓存解决,但属于'带伤运行'。
最佳实践:通过重构(如引入中介者、事件驱动)消除循环依赖,而非依赖框架兜底。
考察点 :IoC 容器原理、设计反模式识别、代码质量意识。
16. Spring Boot Actuator 的 /health 端点如何聚合?
默认情况下,只要有一个 HealthIndicator 返回 DOWN,整体状态就是 DOWN。
自定义聚合策略:通过 ManagementHealthAggregator 实现。
@Component
public class CustomHealthAggregator implements HealthAggregator {
@Override
public Health aggregate (Map<String, Health> healths) {
Status status = Status.UP;
if (isCoreServiceDown(healths)) {
status = Status.DOWN;
}
return new Health .Builder(status).withDetails(healths).build();
}
private boolean isCoreServiceDown (Map<String, Health> healths) {
return healths.get("database" ) != null && healths.get("database" ).getStatus() == Status.DOWN;
}
}
management:
endpoint:
health:
show-details: always
health:
defaults:
aggregator: customHealthAggregator
考察点 :可观测性定制、业务健康模型、运维策略落地。
17. Spring Boot 中如何正确处理分布式事务?
Seata AT/TCC 适用于强一致性场景。
Saga 适用于长事务流程(订单→库存→积分)。
消息队列最终一致适用于日志/通知类。
添加依赖:seata-spring-boot-starter
配置 application.yml 指向 Seata Server
在业务方法加 @GlobalTransactional
金融核心系统 → Seata AT / TCC
电商下单 → Saga(订单→库存→积分)
日志/通知类 → 消息队列最终一致
18. Spring Boot 应用在 Kubernetes 中部署时,如何设计探针?
作用:判断应用是否'活着',失败则重启 Pod。
端点:/actuator/health/liveness
配置建议:
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 60
periodSeconds: 30
failureThreshold: 3
不要包含外部依赖(如 DB 连接),否则网络抖动导致误杀。
作用:判断应用是否'准备好接收流量',失败则从 Service Endpoints 移除。
端点:/actuator/health/readiness
配置建议:
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 1
自动注册 livenessState 和 readinessState 健康指示器。
可通过 /actuator/health 查看状态。
19. Spring Boot 中如何防止 JSON 序列化导致的'无限递归'?
实体类双向关联(如 User ↔ Order)导致 Jackson 序列化栈溢出。
密码、身份证等字段被意外返回。
防止无限递归 :
@JsonManagedReference/@JsonBackReference
@JsonIgnore(简单粗暴)
推荐:使用 DTO 转换,避免直接返回 Entity。
防止敏感信息泄露 :
@JsonIgnore 标记敏感字段
使用 @JsonView 控制不同接口返回字段:
public class Views {
public static class Public {}
public static class Internal extends Public {}
}
public class User {
@JsonView(Views.Public.class)
private String name;
@JsonView(Views.Internal.class)
private String password;
}
@JsonView(Views.Public.class)
@GetMapping("/user")
public User getUser () {...}
@Configuration
public class JacksonConfig {
@Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder () {
return new Jackson2ObjectMapperBuilder ()
.failOnEmptyBeans(false )
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
}
20. Spring Boot 3 中 Jakarta EE 9+ 迁移带来了哪些兼容性问题?
所有 javax.* 包名改为 jakarta.*:
javax.servlet → jakarta.servlet
javax.persistence → jakarta.persistence
javax.validation → jakarta.validation
第三方库未升级:如 MyBatis、旧版 Hibernate 仍依赖 javax.*。
自定义 Filter/Listener:需修改 import 语句。
Swagger/OpenAPI:需升级到支持 Jakarta 的版本(如 Springdoc OpenAPI 1.6+)。
升级到 Spring Boot 2.7,修复所有警告。
升级到 Spring Boot 3.x + JDK 17+。
批量替换 import。
验证所有依赖是否兼容 Jakarta(查看 Maven/Gradle 依赖树)。
考察点 :技术债务管理、大版本迁移、生态兼容性评估。
相关免费在线工具 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