SpringBoot面试题
SpringBoot面试题
什么是Spring Boot?有哪些优点?
Spring Boot 是基于 Spring 框架的快速开发脚手架,它的核心目标是 “简化 Spring 应用的开发、配置和部署流程”。它并非替代 Spring,而是通过 “约定大于配置” 的设计理念,整合 Spring 生态的底层能力(如 IOC、AOP),解决传统 Spring 开发中 “配置繁琐、依赖冲突、部署复杂” 等痛点,让开发者能更专注于业务逻辑,快速搭建可直接运行的生产级应用。
自动配置,减少冗余配置
传统 Spring 开发需要手动写大量 XML 或注解配置(比如配置数据源、MVC 映射等),而 Spring Boot 会根据引入的依赖(如引入
spring-boot-starter-web
)自动推断并配置相关组件(如 Tomcat、DispatcherServlet),无需手动干预,大幅减少配置工作量。起步依赖,解决依赖管理难题
传统开发中,需要手动引入多个相关依赖(如 Web 开发需手动加
spring-web
、tomcat
等),还可能因版本不兼容导致冲突。Spring Boot 将常用依赖打包成 “starter”(如starter-web
、starter-jpa
),引入一个 starter 即可自动获取所有相关依赖及适配版本,避免版本冲突。嵌入式服务器,简化部署流程
默认集成 Tomcat、Jetty 等嵌入式服务器,无需单独安装和配置外部服务器,应用可直接打包成 Jar 包,通过
java -jar
命令启动,部署流程从 “部署 war 包到服务器” 简化为 “一行命令启动”,极大降低部署成本。开发效率高,支持快速迭代
内置热部署(如
spring-boot-devtools
),修改代码后无需重启应用即可生效;同时提供自动配置的监控(如 Actuator)、日志等基础能力,开发者无需从零搭建,能快速聚焦业务开发。无缝兼容 Spring 生态,学习成本低
完全继承 Spring 的核心功能(IOC、AOP 等),熟悉 Spring 的开发者能快速上手;同时兼容 Spring Cloud 等微服务组件,适合微服务架构开发,无需额外学习新框架。
简化测试,支持开箱即用的测试工具
集成
spring-boot-starter-test
,内置 JUnit、Mockito 等测试工具,无需手动配置测试环境,可直接编写单元测试、集成测试,降低测试门槛。
说说Spring Boot的启动流程
sequenceDiagram
participant Main as main()
participant SA as SpringApplication
participant AC as ApplicationContext
participant RS as Runner
Main->>SA: SpringApplication.run()
SA->>SA: 初始化阶段
SA->>AC: 创建应用上下文
AC->>AC: 准备阶段
AC->>AC: 刷新阶段(核心)
AC->>RS: 执行Runner
AC-->>Main: 返回ApplicationContext
启动入口:
SpringApplication.run()
一切从
main
方法调用SpringApplication.run()
开始:1
2
3
4
5
6
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}这个静态方法会:
- 创建
SpringApplication
实例 - 调用其
run()
方法开始启动流程
- 创建
SpringApplication
实例化(初始化准备)SpringApplication.run()
方法首先会创建SpringApplication
实例,此过程主要完成基础配置初始化,具体包括:- 判断应用类型:根据类路径(classpath)是否存在
Servlet
、Reactive
相关类,确定应用类型(如SERVLET
、REACTIVE
),后续将创建对应类型的ApplicationContext
。 - 加载初始化器(Initializer):从
META-INF/spring.factories
中加载所有ApplicationContextInitializer
(上下文初始化器),用于在上下文刷新前对其进行自定义配置(如设置环境变量、注册 Bean 等)。 - 加载监听器(Listener):同样从
META-INF/spring.factories
中加载所有ApplicationListener
(应用监听器),用于监听启动过程中的各种事件(如环境准备完成、上下文刷新完成等),实现扩展逻辑。 - 确定主应用类:通过
main
方法所在的类,定位主应用类(即标注@SpringBootApplication
的类)。
- 判断应用类型:根据类路径(classpath)是否存在
run()
方法执行(容器启动核心流程)SpringApplication
实例创建后,调用其run(args)
方法,开始容器启动,核心步骤如下:步骤 1:启动计时器与发布启动事件
- 启动计时器(
StopWatch
),用于记录启动耗时。 - 发布
ApplicationStartingEvent
事件(启动开始),所有监听器可响应此事件(如初始化日志系统)。
- 启动计时器(
步骤 2:准备环境(Environment)
创建并配置
Environment
(环境对象),包含:- 整合命令行参数(
args
)、系统变量、环境变量、配置文件(application.yml/properties
)等配置源。 - 激活指定的配置文件(如
spring.profiles.active=dev
)。
- 整合命令行参数(
发布
ApplicationEnvironmentPreparedEvent
事件(环境准备完成),监听器可在此阶段修改环境配置(如动态添加配置)。
步骤 3:打印 Banner(可选)
- 读取
banner.txt
(默认在classpath
下),打印启动图案(可通过spring.banner.location
自定义,或关闭spring.main.banner-mode=off
)。
- 读取
步骤 4:创建 ApplicationContext(应用上下文)
- 根据之前判断的应用类型,创建对应的
ApplicationContext
(如 Servlet 应用对应AnnotationConfigServletWebServerApplicationContext
)。 - 为上下文设置环境(
Environment
)、注册主应用类为配置类。
- 根据之前判断的应用类型,创建对应的
步骤 5:准备 ApplicationContext(上下文预处理)
- 应用初始化器(
ApplicationContextInitializer
):调用所有初始化器的initialize(context)
方法,对上下文进行预处理(如注册 BeanDefinition、设置上下文属性)。 - 发布
ApplicationContextInitializedEvent
事件(上下文初始化完成)。 - 注册特殊 Bean:如将命令行参数
args
封装为CommandLineArgs
对象并注册到容器。
- 应用初始化器(
步骤 6:刷新 ApplicationContext(核心中的核心)
此步骤复用了 Spring Framework 的
AbstractApplicationContext.refresh()
逻辑,是容器初始化的核心,主要包括:- BeanFactory 初始化:创建
DefaultListableBeanFactory
作为 Bean 容器。 - 加载 Bean 定义:扫描主类所在包及子包(基于
@ComponentScan
),解析@Component
、@Service
等注解的类,将其注册为BeanDefinition
(Bean 定义)。 - 执行自动配置:通过
@EnableAutoConfiguration
触发,从META-INF/spring.factories
加载AutoConfiguration
类,根据条件(@Conditional
)注册符合条件的 Bean(如数据源、Web 服务器等)。 - 初始化非懒加载单例 Bean:实例化并初始化所有非懒加载的单例 Bean(依赖注入在此阶段完成)。
- 启动嵌入式服务器:对于 Web 应用,会触发
ServletWebServerFactory
(如TomcatServletWebServerFactory
)的 Bean,创建并启动嵌入式服务器(Tomcat/Netty 等)。 - 发布
ContextRefreshedEvent
事件(上下文刷新完成)。
- BeanFactory 初始化:创建
步骤 7:刷新后处理
执行
ApplicationRunner
和CommandLineRunner
:容器刷新完成后,自动调用所有实现这两个接口的 Bean 的run()
方法,用于执行启动后的初始化逻辑(如数据加载、缓存预热)。- 区别:
ApplicationRunner
接收ApplicationArguments
(包装后的参数),CommandLineRunner
接收原始String[] args
。
- 区别:
步骤 8:启动完成
- 发布
ApplicationStartedEvent
(容器启动完成)和ApplicationReadyEvent
(应用可接收请求)事件。 - 停止计时器,打印启动耗时。
- 返回创建好的
ApplicationContext
,应用正式启动完成。
- 发布
讲述一下 SpringBoot 自动装配原理
自动装配可以简单理解为:通过注解或者一些简单的配置就能在 Spring Boot 的帮助下实现某块功能。
SpringBoot 自动装配的核心是:
- 基于 SPI 机制:通过spring.factories发现候选配置类。
- 条件化加载:使用@Conditional系列注解动态决定配置类是否生效。
- 可覆盖设计:默认配置可被用户自定义配置轻松替换。
一、核心设计思想
传统 Spring 开发需要手动配置大量 Bean(如数据源、WebMvc 配置),而 SpringBoot 通过自动装配实现:
- 无配置启动:只需添加 starter 依赖(如
spring-boot-starter-web
),无需 XML 或 JavaConfig,应用即可自动配置并启动。 - 条件化装配:根据类路径(Classpath)中存在的类、配置属性等条件,动态决定是否加载某个配置。
- 可定制化:自动配置并非强制,用户可通过自定义 Bean 或配置文件覆盖默认配置。
二、实现机制:基于 SPI 的自动发现
SpringBoot 的自动装配基于 Java 的服务发现机制(SPI, Service Provider Interface),核心流程如下:
1. 启动入口:@SpringBootApplication
注解
主类上的@SpringBootApplication
是自动装配的起点,它是一个组合注解:
1 |
|
2. 核心注解:@EnableAutoConfiguration
这个注解导入了AutoConfigurationImportSelector
类,负责加载自动配置类:
1 |
|
3. 自动配置类加载:spring.factories
AutoConfigurationImportSelector
会从META-INF/spring.factories
(Spring Boot 2.7+ 以后从 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
)文件中读取所有自动配置类:
1 | # spring-boot-autoconfigure-*.jar/META-INF/spring.factories |
每个自动配置类(如WebMvcAutoConfiguration
)都是一个完整的 JavaConfig,包含多个 Bean 定义。
三、条件化装配:@Conditional
注解
自动配置类并非一定会生效,而是通过@Conditional
及其衍生注解进行条件判断:
注解 | 作用场景 |
---|---|
@ConditionalOnClass |
当类路径中存在指定类时生效 |
@ConditionalOnMissingClass |
当类路径中不存在指定类时生效 |
@ConditionalOnBean |
当容器中存在指定 Bean 时生效 |
@ConditionalOnMissingBean |
当容器中不存在指定 Bean 时生效 |
@ConditionalOnProperty |
当配置文件中存在指定属性且值符合条件时生效 |
@ConditionalOnWebApplication |
当应用是 Web 应用时生效 |
示例:DataSourceAutoConfiguration
中的条件装配
1 |
|
四、自定义与覆盖机制
自动配置提供默认实现,但用户可通过以下方式覆盖:
1. 自定义 Bean
在@Configuration
类中手动定义 Bean,会优先于自动配置中的 Bean:
1 |
|
2. 配置文件属性
通过application.properties
或application.yml
修改默认配置:
1 | spring: |
3. 排除特定自动配置类
在@SpringBootApplication
中排除不需要的自动配置:
1 |
|
五、自动配置执行流程
- 启动入口:
SpringApplication.run()
触发自动装配流程。 - 加载自动配置类:
AutoConfigurationImportSelector
从spring.factories
读取候选配置类。 - 筛选有效配置类:通过
@Conditional
注解过滤不符合条件的配置类。 - 加载有效配置类:将符合条件的配置类注册到 Spring 容器,完成 Bean 的自动装配。
如何自定义一个 SpringBoot Srarter?
一、Starter 的核心组成
一个完整的 Starter 通常包含 3 部分:
- 自动配置模块:包含
@Configuration
配置类,负责自动装配 Bean。 - 配置属性类:通过
@ConfigurationProperties
绑定用户配置(如application.properties
中的属性)。 - META-INF/spring.factories:注册自动配置类,让 SpringBoot 启动时能扫描到。
二、自定义 Starter 的步骤(以 “demo-starter” 为例)
步骤 1:创建 Starter 项目(Maven/Gradle)
推荐使用 Maven,项目结构如下:
1 | demo-spring-boot-starter/ |
步骤 2:配置 pom.xml 依赖
核心依赖包括:
spring-boot-autoconfigure
:提供自动配置基础能力。spring-boot-configuration-processor
:可选,用于生成配置元数据(IDE 会提示配置项)。
1 | <dependencies> |
注意:
- 避免引入不必要的依赖,防止版本冲突(使用
optional=true
控制传递)。 - Starter 命名规范:官方建议第三方 Starter 命名为
xxx-spring-boot-starter
(区分官方的spring-boot-starter-xxx
)。
步骤 3:定义配置属性类(绑定用户配置)
通过@ConfigurationProperties
将用户在application.properties
中的配置(如demo.prefix
)绑定到 Java 类,供自动配置类使用。
1 | // com.demo.properties.DemoProperties.java |
用户使用时可在配置文件中自定义:
1 | # application.properties |
步骤 4:编写自动配置类(核心逻辑)
自动配置类负责根据条件(如@Conditional
)装配 Bean,依赖步骤 3 的配置属性。
1 | // com.demo.config.DemoAutoConfiguration.java |
关键注解说明:
@Configuration
:标记为配置类。@EnableConfigurationProperties
:使DemoProperties
生效,可被注入。@ConditionalOnProperty
:根据配置属性决定是否启用配置。@ConditionalOnMissingBean
:若用户手动定义了DemoService
,则不使用自动配置的 Bean(允许覆盖)。
步骤 5:注册自动配置类(spring.factories)
SpringBoot 启动时会通过 SPI 机制扫描META-INF/spring.factories
,因此需要在此文件中注册自动配置类。
创建src/main/resources/META-INF/spring.factories
:
1 | # 格式:key为EnableAutoConfiguration的全路径,value为自动配置类的全路径(多个用逗号分隔) |
步骤 6:生成配置元数据(提升用户体验)
若引入了spring-boot-configuration-processor
,编译时会自动生成META-INF/spring-configuration-metadata.json
,IDE(如 IDEA)会根据该文件提示配置项(如demo.prefix
的说明)。
如需自定义元数据(如描述),可创建src/main/resources/META-INF/additional-spring-configuration-metadata.json
:
1 | { |
步骤 7:打包发布
执行mvn clean install
,将 Starter 安装到本地仓库(或发布到私服),供其他项目引入。
三、测试自定义 Starter
创建一个测试项目, pom.xml 文件中引入自定义 Starter 依赖,验证自动配置是否生效。
1. pom.xml 文件中引入依赖
1 | <dependency> |
2. 编写测试代码
在application.properties
配置文件中配置:
1 | demo.prefix=myPrefix |
测试类中:
1 |
|
3. 验证覆盖机制
手动定义DemoService
,验证是否覆盖自动配置:
1 |
|
测试结果应为customPrefix:test
,说明覆盖生效。
四、总结
自定义 Starter 的核心流程是:
- 定义配置属性:通过
@ConfigurationProperties
绑定用户配置。 - 编写自动配置类:用
@Conditional
控制 Bean 的装配条件,支持用户覆盖。 - 注册配置类:通过
spring.factories
让 SpringBoot 扫描到。