Spring Boot 面试题

1. 什么是 Spring Boot?

傻瓜理解版

Spring 本身很好用,但是配置比较麻烦。

比如以前创建一个 Spring Web 项目,你要配很多东西:

Spring + Spring MVC + Tomcat + 数据库 + JSON + 各种 XML 配置……

Spring Boot 就像一个**“帮你把 Spring 项目快速组装起来的工具箱”**。

你只需要告诉它:

“我要做一个 Web 项目。”

它就根据你引入的依赖,自动帮你配置很多东西。

所以可以把 Spring Boot 理解成:

让 Spring 项目变得更容易创建、更容易配置、更容易运行。

面试专业版

Spring Boot 是基于 Spring Framework 的快速开发框架,核心目标是简化 Spring 应用的创建和配置。它通过 自动配置、Starter 依赖、嵌入式 Web 容器以及外部化配置 等机制,减少大量样板配置,使 Spring 应用能够快速启动和部署。


2. 说说 Spring Boot 的启动流程?

傻瓜理解版

可以把 Spring Boot 启动想象成“开一家餐厅”。

第一步:

main() 方法启动程序。

第二步:

Spring Boot 创建一个 SpringApplication

第三步:

看看你这是普通 Java 程序、Web 程序还是响应式 Web 程序。

第四步:

创建 Spring 容器,也就是 ApplicationContext

第五步:

加载配置文件、扫描 Bean、执行自动配置。

第六步:

如果是 Web 项目,就启动 Tomcat/Jetty 等服务器。

第七步:

执行 CommandLineRunner​、ApplicationRunner

最后:

发布 ApplicationReadyEvent,表示应用基本准备好了。

整个过程可以记成:

main()
  ↓
SpringApplication.run()
  ↓
读取配置
  ↓
创建 Spring 容器
  ↓
加载 Bean
  ↓
自动配置
  ↓
启动 Web 容器
  ↓
执行 Runner
  ↓
ApplicationReadyEvent
  ↓
项目启动完成

Spring Boot 官方文档也明确给出了启动事件的大体顺序:ApplicationStartingEvent​ → ApplicationEnvironmentPreparedEvent​ → ApplicationContextInitializedEvent​ → ApplicationPreparedEvent​ → ApplicationStartedEvent​ → Runner → ApplicationReadyEvent​。(Home)

面试专业版

Spring Boot 的启动入口通常是 SpringApplication.run()​。启动过程中首先创建并配置 SpringApplication​,准备 Environment​,然后根据应用类型创建对应的 ApplicationContext​。接着加载 Bean 定义并执行组件扫描和自动配置,通过 refresh()​ 完成 Bean 初始化以及 WebServer 启动,之后执行 ApplicationRunner​ 和 CommandLineRunner​,最后发布 ApplicationReadyEvent​,表示应用进入就绪状态。(Home)


3. Spring Boot 的核心特性有哪些?

傻瓜理解版

你记住五个东西就行:

自动配置

Spring Boot 根据项目情况,自动帮你配置。

Starter

一组已经帮你打包好的依赖。

嵌入式 Web 容器

不需要自己装 Tomcat,也可以直接跑。

外部化配置

数据库地址、端口、Redis 地址等可以放配置文件或环境变量。

Actuator

帮你监控和管理项目。

所以一句话:

自动配、少配置、直接跑、方便改、方便监控。

面试专业版

Spring Boot 的核心特性主要包括 ​Auto Configuration 自动配置、Starter 起步依赖、Embedded Server 嵌入式容器、Externalized Configuration 外部化配置以及 Actuator 生产级监控管理能力​。这些特性共同降低了 Spring 应用的配置和部署成本。(Home)


4. Spring Boot 是如何通过 main 方法启动 Web 项目的?

傻瓜理解版

你写:

public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
}

看起来只是调用了一行代码。

实际上这一行干了很多事情。

因为:

@SpringBootApplication

里面包含了:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

所以 Spring Boot 会:

扫描你的代码 → 自动配置 → 创建 Spring 容器 → 创建 Web 容器 → 启动服务器。

因此你执行:

java -jar xxx.jar

程序就能直接启动。

面试专业版

Spring Boot 通过 SpringApplication.run()​ 作为应用启动入口。@SpringBootApplication​ 是一个组合注解,包含 @SpringBootConfiguration​、@EnableAutoConfiguration​ 和 @ComponentScan​。对于 Servlet Web 应用,Spring Boot 会创建 ServletWebServerApplicationContext​,自动配置 ServletWebServerFactory​,通常启动嵌入式 Tomcat,并完成 Spring MVC 相关组件的初始化。(Home)


5. SpringBoot 是如何实现自动配置的?

傻瓜理解版

这是非常重要的一题。

假设你项目里面加入了:

spring-boot-starter-data-redis

Spring Boot 看到:

“哦,你项目要用 Redis。”

它就会尝试帮你创建:

RedisTemplate、连接工厂等相关 Bean。

但是它不是无脑创建。

比如:

你自己已经定义了一个 RedisTemplate。

Spring Boot 会说:

“你自己已经配了,那我就不抢你的。”

这就是自动配置。

核心思想:

你不配置,我帮你配置;你自己配置,我就让位。

面试专业版

Spring Boot 的自动配置主要通过 @EnableAutoConfiguration​ 实现。它根据 classpath 中存在的依赖以及当前 Spring 容器中的 Bean 和配置属性,选择相应的自动配置类,并通过各种 @Conditional​ 条件进行判断,例如 @ConditionalOnClass​、@ConditionalOnMissingBean​、@ConditionalOnProperty​。如果用户已经提供了对应 Bean,自动配置通常会通过条件判断进行 back off,从而允许用户覆盖默认配置。(Home)

你最好顺便记住

@EnableAutoConfiguration
        ↓
找到自动配置类
        ↓
Conditional 条件判断
        ↓
满足条件
        ↓
创建 Bean

6. Spring Boot 支持哪些嵌入 Web 容器?

傻瓜理解版

最常见的是:

Tomcat

还有:

Jetty

如果是响应式 Web,也经常见到:

Netty

你可以理解成:

Spring Boot 自己背着一个“小型服务器”,所以不用把项目扔到外部 Tomcat 里面才能运行。

Servlet Web 应用中,Spring Boot 官方当前文档重点介绍嵌入式 Tomcat 和 Jetty;WebFlux 通常使用 Reactor Netty。(Home)

面试专业版

Spring Boot 支持嵌入式 Web 容器。对于 Servlet Web 应用,常见的是 ​Tomcat 和 Jetty​;对于 Spring WebFlux,默认通常使用 ​Reactor Netty​。通过 Starter 和自动配置可以自动创建对应的 WebServer。(Home)


7. application.properties 和 application.yml 的区别是什么?

傻瓜理解版

本质上:

都是配置文件。

区别主要是写法不同。

.properties

server.port=8080
spring.datasource.url=xxx
spring.datasource.username=root

.yml

server:
  port: 8080

spring:
  datasource:
    url: xxx
    username: root

YAML 更适合:

配置很多、层级很多的项目。

面试专业版

application.properties​ 和 application.yml​ 都可以用于 Spring Boot 外部化配置,功能上没有本质区别,主要区别是语法和可读性。Properties 使用 key=value​,YAML 通过缩进表达层级结构,更适合复杂配置。实际项目通常选择一种格式统一使用;如果同一位置同时存在 .properties​ 和 YAML 配置,Spring Boot 会让 .properties​ 优先。(Home)


8. 如何在 Spring Boot 中定义和读取自定义配置?

傻瓜理解版

比如我自己定义:

my:
  name: 张三
  age: 20

我要在 Java 里面拿出来。

最简单:

@Value("${my.name}")
private String name;

还有一种更适合大量配置的方式:

@ConfigurationProperties(prefix = "my")

可以理解为:

@Value 适合拿一个值。

@ConfigurationProperties 适合把一整组配置装进一个 Java 对象。

面试专业版

Spring Boot 支持多种方式读取自定义配置,常见的有 @Value​、Environment​ 和 @ConfigurationProperties​。其中 @Value​ 更适合少量配置项的注入,而 @ConfigurationProperties​ 可以将具有相同前缀的配置批量绑定到 Java Bean,类型安全性和可维护性更好。(Home)


9. Spring Boot 配置文件加载优先级你知道吗?

傻瓜理解版

这个问题很容易背乱。

你只需要记住一个最核心的原则:

后来的覆盖前面的。

比如:

application.yml
    ↓
外部 application.yml
    ↓
环境变量
    ↓
命令行参数

通常越靠后的配置,优先级越高。

例如:

server.port=8080

配置文件说:

8080。

但是启动:

java -jar app.jar --server.port=9000

命令行说:

9000。

那么最后就是:

9000。

Spring Boot 官方当前的完整 PropertySource​ 顺序还包含默认属性、@PropertySource​、Config Data、环境变量、Java System properties、JNDI、Servlet 参数、SPRING_APPLICATION_JSON​、命令行参数以及测试专用属性等;命令行参数优先级高于文件配置。(Home)

面试专业版

Spring Boot 的配置采用 PropertySource​ 优先级机制,​后加载的配置可以覆盖前面配置的同名属性​。从实际开发最常见的角度来说,可以重点记住:配置文件中的默认配置可以被外部配置、环境变量以及命令行参数覆盖,其中命令行参数具有较高优先级。配置文件本身也存在 classpath 内配置和 jar 外配置的覆盖关系,profile-specific 配置会覆盖普通配置。(Home)

面试千万别这样答

“application.yml 比 application.properties 优先级高。”

这个说法不准确。


10. Spring Boot 打成的 jar 和普通的 jar 有什么区别?

傻瓜理解版

普通 Jar 可以理解成:

“我只是把自己的 Java class 打包起来。”

Spring Boot Jar 则可以理解成:

“我把自己的代码 + 第三方依赖 + Spring Boot 启动器一起装进去了。”

例如:

my-app.jar

BOOT-INF/
 ├── classes/
 │    └── 你的代码
 │
 └── lib/
      ├── spring.jar
      ├── jackson.jar
      └── mysql.jar

所以你可以:

java -jar my-app.jar

直接运行。

Spring Boot 可执行 Jar 会使用 Boot Loader,并把应用类和依赖放入 BOOT-INF/classes​、BOOT-INF/lib​ 等目录。(Home)

面试专业版

普通 Jar 主要用于 Java 类和资源的打包,本身不一定包含运行所需的全部依赖。Spring Boot 可执行 Jar 会通过 spring-boot-maven-plugin​ 或 Gradle Plugin 将应用类、依赖以及 Spring Boot Loader 打包到统一的可执行归档中,并通过 java -jar​ 启动。其核心特点是支持嵌套 Jar,例如依赖位于 BOOT-INF/lib​,应用类位于 BOOT-INF/classes​。(Home)


11. Spring Boot 是否可以使用 XML 配置?

傻瓜理解版

当然可以。

Spring Boot 并不是:

“XML 禁止入场。”

只是现在更加推荐 Java 注解配置。

例如:

@ImportResource("classpath:application.xml")

就可以加载 XML。

所以:

可以用,只是不推荐把整个项目全部搞成 XML。

面试专业版

Spring Boot 支持 XML 配置,但官方更推荐使用 Java Configuration。对于已有 XML 配置的项目,可以通过 @ImportResource​ 将 XML 配置文件加载到 Spring 容器中,从而实现 XML 与 Java Configuration 混合使用。(Home)


12. SpringBoot 默认同时可以处理的最大请求数是多少?

傻瓜理解版

这道题有个坑。

Spring Boot 没有一个统一的“最大请求数 = xxx”这种固定答案。

因为实际处理请求的是:

Tomcat / Jetty / Netty。

如果是传统 Spring MVC + Tomcat,那么默认情况下:

Tomcat worker threads 最大通常是 ​200

所以很多面试题答案会说:

默认最大 200 个线程同时处理请求。

但这不等于:

“服务器最多只能有 200 个请求。”

例如当前 Tomcat 默认最大连接数是 8192,而工作线程默认最大 200。连接数和同时执行请求数不是一回事。(Home)

面试专业版

Spring Boot 本身并没有固定的最大并发请求数,具体取决于底层 Web Server 和线程模型。以 Spring MVC + Tomcat 为例,默认 worker thread 最大值通常为 ​200​,因此在传统同步阻塞模型下,可以理解为默认最多约 200 个请求同时占用工作线程;另外 Tomcat 的最大连接数默认可以达到 8192,因此​最大连接数、同时处理请求数和 QPS 是三个不同概念​。(Home)

这一题很重要

你面试时最好回答:

“没有固定的最大请求数。如果是默认 Tomcat,可以重点看 server.tomcat.threads.max,默认 200。”

比直接说:

“Spring Boot 最大处理 200 个请求。”

专业很多。


13. 如何理解 Spring Boot 中的 starter?

傻瓜理解版

Starter 就像:

“套餐。”

比如你想做 Web:

spring-boot-starter-web

它会帮你准备 Web 开发需要的一批依赖。

你不用一个一个找:

Spring MVC
Jackson
Tomcat
……

相当于:

我要吃 Web 套餐。

Starter:

好的,相关东西给你打包好了。

面试专业版

Starter 本质上是一组方便管理的依赖描述,它通过 Maven 或 Gradle 的传递依赖机制,将某一技术栈所需的相关依赖组合起来。例如 spring-boot-starter-web​ 用于快速引入 Spring Web 开发相关依赖。Starter 本身主要解决的是​依赖管理和快速集成问题​,自动配置则主要由 Spring Boot 的 Auto Configuration 机制负责,两者经常配合使用。(Home)

非常容易被追问

Starter ≠ 自动配置。

记住:

Starter
↓
帮你把依赖带进来

Auto Configuration
↓
根据依赖和条件帮你配置 Bean

14. Spring Boot 如何处理跨域请求(CORS)?

傻瓜理解版

假设:

前端:http://localhost:5173
后端:http://localhost:8080

虽然都是你自己的电脑,但是端口不同。

浏览器会认为:

“这两个不是一个来源。”

于是前端访问后端可能被浏览器拦截。

这就是跨域。

Spring 可以告诉浏览器:

“允许这个前端访问我。”

最常用:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:5173")
                .allowedMethods("*");
    }
}

Spring MVC 官方支持通过 WebMvcConfigurer#addCorsMappings​ 配置 CORS。(Home)

面试专业版

Spring Boot 中通常通过 Spring MVC 的 CORS 机制处理跨域,可以在 WebMvcConfigurer​ 中重写 addCorsMappings(),配置允许的 Origin、Method、Header 以及是否允许携带 Cookie 等。对于前后端分离项目,也可以在网关层统一处理 CORS。


15. 在 Spring Boot 中你是怎么使用拦截器的?

傻瓜理解版

拦截器就像:

小区门卫。

用户请求进来:

请求
 ↓
门卫检查
 ↓
合格 → 放进去
不合格 → 拦下来

例如:

/token

没有登录:

不让访问。

所以可以用拦截器做:

登录检查、权限检查、日志记录等。

代码:

@Component
public class LoginInterceptor implements HandlerInterceptor {
}

然后:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login");
    }
}

Spring MVC 官方就是通过 WebMvcConfigurer#addInterceptors​ 注册 HandlerInterceptor​。(Home)

面试专业版

在 Spring Boot 中,我通常通过实现 HandlerInterceptor​ 编写拦截逻辑,然后通过 WebMvcConfigurer#addInterceptors()​ 注册,并使用 addPathPatterns()​ 和 excludePathPatterns()​ 配置拦截及放行路径。常见应用场景包括登录认证、权限校验、请求日志和请求耗时统计等。(Home)


16. SpringBoot 中如何实现定时任务?

傻瓜理解版

例如:

每天凌晨 2 点执行一次清理任务。

Spring Boot 可以使用:

@EnableScheduling

开启定时任务。

然后:

@Scheduled(cron = "0 0 2 * * ?")
public void clean() {
    // ...
}

意思就是:

到时间自动执行。

面试专业版

Spring Boot 中可以使用 Spring Scheduling 实现定时任务。首先通过 @EnableScheduling​ 开启调度功能,然后在任务方法上使用 @Scheduled​,可以通过 fixedRate​、fixedDelay​ 或 cron​ 指定执行策略。生产环境中还需要关注任务并发、线程池以及集群部署时的重复执行问题。(Home)

你以后做微服务时特别要注意

如果:

服务器 A
服务器 B
服务器 C

三个实例都有:

@Scheduled

那么默认情况下:

三个实例都可能执行。

所以集群定时任务通常还需要:

分布式锁 / XXL-JOB / Quartz 集群等。


17. 什么是 Spring Actuator?它有什么优势?

傻瓜理解版

Actuator 可以理解成:

给项目装了一套“仪表盘”。

它可以帮助你看:

项目活没活着?
数据库正常吗?
JVM怎么样?
内存怎么样?
有哪些 Bean?
有哪些配置?

例如:

/actuator/health

可以检查健康状态。

面试专业版

Spring Boot Actuator 是 Spring Boot 提供的生产级监控和管理模块,通过 Endpoint 暴露应用的健康状态、指标、环境信息、Bean 等运行信息。它能够辅助应用的监控、故障排查和运维管理。例如常见的 /actuator/health​、/actuator/metrics​ 等端点。默认 Web Endpoint 基础路径通常是 /actuator​,具体暴露哪些端点可以进行配置。(Home)


18. Spring Boot 2.x 与 1.x 版本有哪些主要的改进和区别?

傻瓜理解版

你没必要把 1.x → 2.x 的所有变化全部背下来。

重点记三个:

第一:Java 版本提高

Spring Boot 2.0 最低基线变成:

Java 8

而 1.x 时代还支持更老的 Java。(Home)

第二:支持 WebFlux

2.x 开始加入:

Spring WebFlux

也就是响应式 Web。

第三:性能和生态进行了升级

比如:

默认数据库连接池从 Tomcat Pool 切换到了 HikariCP。

同时 Actuator 等也进行了较大调整。(GitHub)

面试专业版

Spring Boot 2.x 相比 1.x 的主要变化包括:首先以 Java 8 作为最低基线;其次基于 Spring Framework 5 引入了对 Spring WebFlux 响应式编程的支持;另外默认数据库连接池切换为 ​HikariCP​,Actuator、配置以及嵌入式 Web Server 等多个模块也进行了升级和调整。(Home)

记忆口诀

1.x → 2.x

Java 8
WebFlux
HikariCP
生态升级

19. Spring Boot 3.x 与 2.x 版本有哪些主要的改进和区别?

傻瓜理解版

这道题非常重要。

最容易记:

Boot 2.x
   ↓
Boot 3.x

第一件大事:Java 17

Spring Boot 3:

最低 Java 17。

(GitHub)

第二件大事:javax → jakarta

以前:

javax.servlet
javax.persistence
javax.validation

后来:

jakarta.servlet
jakarta.persistence
jakarta.validation

这是升级 Boot 3 时非常重要的兼容性变化。(GitHub)

第三件大事:Spring Framework 6

Boot 3 基于:

Spring Framework 6

(GitHub)

第四件大事:Hibernate 6

如果项目使用 JPA:

Boot 3 默认使用 Hibernate 6 系列。

(GitHub)

第五件大事:可观测性和 Native Image

Boot 3 对:

Observability

以及:

GraalVM Native Image

有更好的支持。(Home)

面试专业版

Spring Boot 3.x 相比 2.x 最大的升级点主要有几个:第一,最低 JDK 要求提升到 ​Java 17​;第二,底层升级到 ​Spring Framework 6​;第三,Java EE API 全面迁移到 ​Jakarta EE​,因此大量 javax.*​ 包需要迁移到 jakarta.*​;第四,默认生态组件进行了升级,例如 Hibernate 6;另外 Boot 3 对 Observability、GraalVM Native Image 等现代部署和监控能力进行了增强。(Home)

这个答案非常适合你背

Boot 3 最大的变化就是:Java 17、Spring 6、javax 改 jakarta,以及 Hibernate 6、可观测性和 Native Image 支持增强。


20. 说说你对 Spring Boot 事件机制的了解?

傻瓜理解版

事件机制可以理解成:

一个人喊话,其他人自己决定要不要听。

比如:

用户注册成功
      ↓
发布“用户注册成功事件”
      ↓
邮件服务收到 → 发邮件
积分服务收到 → 加积分
优惠券服务收到 → 发优惠券

注册业务本身不用直接调用三个服务。

就相当于:

“我只负责宣布发生了什么,谁关心谁自己处理。”

面试专业版

Spring Boot 事件机制基于 Spring Framework 的 ApplicationEventPublisher​ 和事件监听器实现。业务代码通过发布事件实现生产者与消费者解耦,监听方可以通过 @EventListener​ 或 ApplicationListener​ 接收事件。Spring Boot 在启动过程中还会发布多个生命周期事件,例如 ApplicationStartedEvent​ 和 ApplicationReadyEvent​。(Home)


21. 在 Spring Boot 中如何实现多数据源配置?

傻瓜理解版

比如一个项目有:

数据库 A:用户数据库
数据库 B:订单数据库

你不能只配置一个:

DataSource

而应该:

DataSource A
DataSource B

然后告诉程序:

这个业务去 A。

那个业务去 B。

一般通过:

@Qualifier
@Primary

区分。

如果是 MyBatis,还要进一步区分:

SqlSessionFactory、Mapper 扫描路径、事务管理器。

面试专业版

Spring Boot 多数据源的核心思路是定义多个 DataSource​ Bean,并分别绑定不同的配置前缀,再使用 @Primary​ 和 @Qualifier​ 解决默认数据源和依赖注入歧义。如果使用 MyBatis,还需要分别配置对应的 SqlSessionFactory​、SqlSessionTemplate​ 和 Mapper 扫描范围;如果涉及事务,还需要正确配置相应的数据源事务管理器。(Home)


22. Spring Boot 中如何实现异步处理?

傻瓜理解版

正常情况下:

用户请求
 ↓
执行任务 A
 ↓
执行任务 B
 ↓
执行任务 C
 ↓
返回

如果 B 很慢,用户就要一直等。

异步就是:

用户请求
 ↓
任务 A
 ↓
把任务 B 扔到线程池
 ↓
继续往下执行
 ↓
返回

任务 B:

在线程池里自己慢慢干。

最常见的就是:

@EnableAsync

然后:

@Async
public void sendEmail() {
}

面试专业版

Spring Boot 中常用 @EnableAsync​ 开启异步执行能力,然后在方法上使用 @Async​,由 Spring 管理的 TaskExecutor​ 在线程池中异步执行任务。实际项目中通常会自定义线程池,合理设置核心线程数、最大线程数、队列和拒绝策略,避免直接依赖默认线程池导致资源不可控。(Home)


23. 如何在 SpringBoot 启动时执行特定代码?有哪些方式?

傻瓜理解版

有很多办法。

最常见可以记:

方法 1:@PostConstruct

Bean 创建完成之后执行。

@PostConstruct
public void init() {
}

理解:

“这个对象造好了,马上执行。”


方法 2:CommandLineRunner

项目启动过程中执行:

@Component
public class MyRunner implements CommandLineRunner {

    @Override
    public void run(String... args) {
    }
}

方法 3:ApplicationRunner

CommandLineRunner 类似,只不过参数处理更方便。


方法 4:监听 ApplicationReadyEvent

等整个应用准备完成之后:

@EventListener(ApplicationReadyEvent.class)
public void init() {
}

面试专业版

Spring Boot 启动阶段执行自定义代码有多种方式。Bean 初始化阶段可以使用 @PostConstruct​;应用启动阶段可以使用 CommandLineRunner​ 或 ApplicationRunner​;如果希望在应用基本完成启动、进入 Ready 状态后执行,可以监听 ApplicationReadyEvent​。其中 CommandLineRunner​ 和 ApplicationRunner​ 会在 ApplicationStartedEvent​ 之后、ApplicationReadyEvent​ 之前执行。(Home)

你面试最好顺便说一句

“如果代码依赖整个应用已经启动完成,我更倾向于使用 ApplicationReadyEvent​;如果只是启动阶段初始化任务,可以使用 CommandLineRunner​ 或 ApplicationRunner。”


24. SpringBoot(Spring)中为什么不推荐使用 @Autowired?

傻瓜理解版

其实不是:

@Autowired 不能用。

而是:

不太推荐字段注入。

例如:

@Autowired
private UserService userService;

看起来简单,但有几个问题。

第一:

这个类需要什么依赖,不够明显。

第二:

字段不能方便地使用 final

第三:

单元测试不方便。

推荐:

private final UserService userService;

public UserController(UserService userService) {
    this.userService = userService;
}

这样一看就知道:

这个 Controller 离不开 UserService。

Spring 官方也明确说明,如果 Bean 只有一个构造方法,可以直接使用该构造方法,不需要额外写 @Autowired​。(Home)

面试专业版

并不是禁止使用 @Autowired​,而是通常不推荐 ​字段注入​。构造器注入具有依赖显式、支持 final​、便于单元测试以及更符合依赖不可变原则等优势。对于只有一个构造方法的 Bean,Spring 可以自动使用该构造方法,因此无需额外添加 @Autowired​。(Home)

这一题一定要注意

不要回答:

“Spring Boot 不推荐 @Autowired。”

更准确的是:

不推荐字段注入,更推荐构造器注入。

因为:

@Autowired
public XxxService(UserService service) {}

本身就是构造器注入,也没有什么问题。


25. @Async 什么时候会失效?

傻瓜理解版

这是一个非常经典的坑。

最经典情况:

@Service
public class TestService {

    public void a() {
        b();
    }

    @Async
    public void b() {
    }
}

你以为:

a()
 ↓
b()
 ↓
异步

但实际上:

可能不会异步。

为什么?

因为 @Async​ 是靠 Spring AOP 代理实现的。

但是:

this.b();

是:

自己调用自己。

没有经过 Spring 代理。

所以 @Async 没机会拦截。

可以记:

异步要经过代理,自调用绕过代理。

另外,以下情况也可能有问题:

不是 Spring Bean
没有开启 @EnableAsync
方法/类不适合代理
调用方式绕过 Spring 代理

Spring 官方明确指出,@Async​ 默认采用 proxy 模式,因此​同一个类内部的本地调用不会被代理拦截​。(Home)

面试专业版

@Async​ 基于 Spring AOP Proxy 实现,因此调用必须经过 Spring 代理对象。常见失效场景包括​同类内部自调用、对象没有交给 Spring 容器管理、没有开启 @EnableAsync,以及某些方法或类无法被当前代理机制拦截​。其中最典型的是 self-invocation,因为内部调用不会经过代理,所以异步拦截器不会生效。(Home)

最好再记一句

@Async
  ↓
Spring AOP
  ↓
代理对象
  ↓
线程池

26. Spring Boot 工程启动以后,我希望将数据库中已有的固定内容,打入到 Redis 缓存中,请问如何处理?

傻瓜理解版

你这个需求非常简单。

比如数据库里面有:

商品分类
热门商品
系统字典
固定配置

项目启动之后:

Spring Boot 启动
      ↓
读取数据库
      ↓
拿到固定内容
      ↓
写入 Redis
      ↓
完成缓存预热

最常见的做法:

ApplicationReadyEvent + RedisTemplate

例如:

@Component
public class CacheWarmup {

    private final RedisTemplate<String, Object> redisTemplate;

    public CacheWarmup(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @EventListener(ApplicationReadyEvent.class)
    public void warmUp() {
        // 1. 查询数据库
        // 2. 写入 Redis
    }
}

这样做的意思就是:

应用启动完成 → 从数据库加载固定数据 → 写进 Redis。

面试专业版

我会把这个过程称为 ​Redis 缓存预热​。可以在 Spring Boot 启动阶段通过 ApplicationReadyEvent​、CommandLineRunner​ 或 ApplicationRunner​ 执行初始化逻辑,先从数据库查询需要预热的数据,再通过 RedisTemplate​ 等客户端批量写入 Redis。生产环境还需要考虑幂等性、缓存 Key 设计、TTL、批量写入以及 Redis 不可用时的启动失败策略。(Home)


最后给你一张“面试速记表”🔥

题目 你脑子里首先想到什么
Spring Boot 是什么 让 Spring 更容易开发
启动流程 main → run → 容器 → Bean → WebServer → Ready
核心特性 自动配置 + Starter + 嵌入式容器 + 外部配置 + Actuator
main 怎么启动 Web SpringApplication.run()
自动配置 依赖 + 条件判断 + 自动创建 Bean
Web 容器 Tomcat、Jetty、Netty
properties / yml 都是配置,写法不同
自定义配置 @Value​/@ConfigurationProperties
配置优先级 后面的覆盖前面的
Boot Jar 自己的代码 + 依赖 + Boot Loader
XML 可以,@ImportResource
最大请求数 没有固定值;Tomcat 默认 worker threads 200
Starter 依赖套餐
CORS 解决跨域
Interceptor 请求进来的门卫
定时任务 @EnableScheduling + @Scheduled
Actuator 监控/管理工具
1.x → 2.x Java 8、WebFlux、HikariCP
2.x → 3.x Java 17、Spring 6、javax→jakarta、Hibernate 6
事件 发布者和监听者解耦
多数据源 多个 DataSource + Qualifier/Primary
异步 @EnableAsync + @Async + 线程池
启动执行代码 @PostConstruct / Runner / ReadyEvent
@Autowired 不是不能用,是不推荐字段注入
@Async 失效 最经典:同类自调用绕过代理
Redis 启动缓存 缓存预热

你现在最应该重点背的 8 道

按照 Java 后端面试的实际追问习惯,我建议你把下面 8 道背得最熟:

① 自动配置

@EnableAutoConfiguration
    ↓
找到自动配置类
    ↓
@Conditional 判断
    ↓
满足条件
    ↓
创建 Bean

② Spring Boot 启动流程

main
 ↓
SpringApplication.run()
 ↓
Environment
 ↓
ApplicationContext
 ↓
Bean + AutoConfiguration
 ↓
WebServer
 ↓
Runner
 ↓
ApplicationReadyEvent

③ Starter 和自动配置区别

Starter
= 帮你引入依赖

AutoConfiguration
= 帮你配置 Bean

④ 最大请求数

不要死背:

“200。”

要背:

“Spring Boot 没有统一固定值。传统 Spring MVC + Tomcat 默认 worker threads 是 200,连接数是另外一个概念。”

⑤ Boot 2 → Boot 3

直接背:

Java 17 + Spring 6 + javax → jakarta + Hibernate 6 + Native/Observability 增强。

⑥ @Autowired

直接说:

“不是不推荐 @Autowired,而是不推荐字段注入,更推荐构造器注入。”

⑦ @Async 失效

直接说:

**“**​ @Async基于 Spring AOP 代理,同类内部自调用没有经过代理,所以异步不会生效。”

⑧ Redis 启动缓存

直接说:

“这是缓存预热,可以监听 ApplicationReadyEvent,启动完成后查询数据库,再批量写入 Redis。”

这些答案已经足够覆盖大多数初中级 Java/Spring Boot 面试中的第一轮问答,而且可以继续向 Spring、AOP、线程池、Redis、MySQL 方向展开。