Spring Boot 面试八股文
本文总结了Spring Boot的核心面试考点,包括基础概念、核心原理和自动配置机制。Spring Boot通过自动配置、约定优于配置简化Spring应用开发,整合Spring和Spring MVC,提供嵌入式容器和Starter依赖管理。其核心注解如@SpringBootApplication、@EnableAutoConfiguration等简化配置。自动配置流程包括条件筛选和Bean注册,通
Spring Boot 面试八股文(核心考点汇总)
一、Spring Boot 基础概念
1. 什么是 Spring Boot?它的核心优势是什么?
Spring Boot 是 Spring 生态的“脚手架”,基于 Spring 框架,通过自动配置、约定优于配置(Convention Over Configuration) 简化 Spring 应用的搭建与开发,核心目标是“让开发人员快速启动一个独立、生产级别的 Spring 应用”。
核心优势:
- 简化配置:无需手动编写大量 XML 配置,通过
application.properties/yaml或注解即可完成配置。 - 自动配置:根据依赖的 Jar 包(如引入
spring-boot-starter-web则自动配置 Tomcat、Spring MVC),自动初始化相关 Bean 到 Spring 容器。 - 嵌入式容器:内置 Tomcat、Jetty、Undertow 等 Servlet 容器,无需单独部署 WAR 包,直接运行 Jar 包即可。
- starters 依赖:提供标准化的 starter 依赖(如
spring-boot-starter-data-jpa),一键引入某功能所需的所有依赖,避免依赖冲突。 - 生产级特性:内置健康检查(Actuator)、 metrics 监控、外部化配置等,简化生产环境部署与维护。
- 无代码生成/XML 配置:基于注解驱动,无需手动生成代码或编写 XML。
2. Spring Boot 与 Spring、Spring MVC 的关系?
三者是“包含与扩展”的关系,核心目标都是简化 Java 开发,但定位不同:
- Spring:核心是“依赖注入(DI)”和“面向切面(AOP)”,是整个生态的基础,负责管理 Bean 的生命周期、解耦组件。
- Spring MVC:是 Spring 生态的“Web 模块”,基于 MVC 架构(Model-View-Controller),负责处理 HTTP 请求、路由分发、视图渲染等 Web 开发功能。
- Spring Boot:不是替换 Spring/Spring MVC,而是整合并简化两者的使用——通过自动配置将 Spring MVC 的 DispatcherServlet、Tomcat 等组件自动初始化,开发者无需手动配置即可快速开发 Web 应用。
简单总结:Spring Boot = Spring + Spring MVC + 自动配置 + 嵌入式容器 + Starter 机制。
3. Spring Boot 的核心注解有哪些?各自作用是什么?
Spring Boot 核心注解集中在启动类和配置类,关键注解如下:
| 注解 | 作用 |
|---|---|
@SpringBootApplication |
启动类的“组合注解”,包含 3 个核心注解,是 Spring Boot 应用的入口。 |
@SpringBootConfiguration |
等同于 @Configuration,标记当前类是“配置类”,允许通过 @Bean 注册 Bean。 |
@EnableAutoConfiguration |
开启“自动配置”功能,扫描并加载 META-INF/spring.factories 中的自动配置类。 |
@ComponentScan |
扫描当前包及其子包下的 @Component、@Service、@Controller 等注解,将类注册为 Bean。 |
@Configuration |
标记类为配置类,替代传统 XML 配置文件,内部可通过 @Bean 定义 Bean。 |
@Bean |
在配置类中定义 Bean,等同于 XML 中的 <bean> 标签,默认 Bean 名称为方法名。 |
@Value |
注入外部配置(如 application.yml 中的值),支持 SpEL 表达式(如 ${server.port})。 |
@ConfigurationProperties |
将外部配置(如 application.yml)批量绑定到 Java 类中(需配合 @Component 或 @EnableConfigurationProperties)。 |
二、核心原理:自动配置
自动配置是 Spring Boot 的“灵魂”,面试中高频考察“如何实现自动配置”“如何自定义自动配置”。
1. Spring Boot 自动配置的核心流程是什么?
核心逻辑:根据 classpath 下的依赖(Jar 包),自动加载并初始化对应的配置类,注册所需 Bean 到 Spring 容器,流程如下:
- 启动触发:启动类标注
@SpringBootApplication,其中@EnableAutoConfiguration是自动配置的入口。 - 加载配置类:
@EnableAutoConfiguration会通过SpringFactoriesLoader扫描所有 Jar 包下的META-INF/spring.factories文件,读取其中org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的配置类(如WebMvcAutoConfiguration、DataSourceAutoConfiguration)。 - 条件筛选:自动配置类上通常标注
@Conditional系列注解(如@ConditionalOnClass、@ConditionalOnMissingBean),只有满足条件(如 classpath 存在某类、容器中不存在某 Bean)时,该配置类才会生效。 - 注册 Bean:生效的配置类通过
@Bean注册所需的 Bean 到 Spring 容器(如WebMvcAutoConfiguration注册DispatcherServlet)。 - 配置绑定:通过
@ConfigurationProperties将application.yml中的配置(如spring.mvc前缀的配置)绑定到自动配置类的属性上,实现“配置自定义”。
2. @Conditional 系列注解有哪些?作用是什么?
@Conditional 是“条件注解”,用于控制配置类/Bean 的生效条件,是自动配置的“开关”,常用子类:
| 注解 | 生效条件 |
|---|---|
@ConditionalOnClass |
当 classpath 中存在指定类时生效(如 @ConditionalOnClass(Servlet.class))。 |
@ConditionalOnMissingClass |
当 classpath 中不存在指定类时生效。 |
@ConditionalOnBean |
当 Spring 容器中存在指定 Bean 时生效(如 @ConditionalOnBean(DataSource.class))。 |
@ConditionalOnMissingBean |
当 Spring 容器中不存在指定 Bean 时生效(允许用户自定义 Bean 覆盖默认配置)。 |
@ConditionalOnProperty |
当外部配置(如 application.yml)中存在指定属性,且值匹配时生效(如 @ConditionalOnProperty(prefix = "spring.datasource", name = "enabled", havingValue = "true"))。 |
@ConditionalOnWebApplication |
当应用是 Web 应用(Servlet 或 Reactive)时生效。 |
3. 如何自定义一个 Spring Boot Starter?
Starter 是 Spring Boot 的“依赖封装”,本质是“一组依赖 + 自动配置类”,让用户引入一个 Starter 即可使用某功能(如自定义 mybatis-starter)。自定义步骤如下:
步骤 1:创建 2 个模块(或 1 个模块包含 2 部分)
- Starter 模块:仅包含依赖管理(
pom.xml),不写代码,负责引入核心功能模块和 Spring Boot 自动配置依赖。 - AutoConfigure 模块:包含自动配置类、Bean 定义、
spring.factories文件,是 Starter 的核心逻辑。
步骤 2:配置 Starter 模块的 pom.xml
引入 AutoConfigure 模块和 Spring Boot 自动配置依赖:
<dependencies>
<!-- 引入自动配置模块 -->
<dependency>
<groupId>com.example</groupId>
<artifactId>my-starter-autoconfigure</artifactId>
<version>1.0.0</version>
</dependency>
<!-- Spring Boot 自动配置基础依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!-- 配置绑定依赖(可选,如需读取 application.yml) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
步骤 3:编写 AutoConfigure 模块的核心逻辑
-
配置属性类:通过
@ConfigurationProperties绑定外部配置(如application.yml中的my.starter前缀配置):@ConfigurationProperties(prefix = "my.starter") public class MyStarterProperties { private String name = "default"; // 默认值 private int timeout = 3000; // getter/setter } -
自动配置类:通过
@Configuration和@Conditional定义 Bean,依赖配置属性类:@Configuration @EnableConfigurationProperties(MyStarterProperties.class) // 启用配置属性绑定 @ConditionalOnClass(MyService.class) // 当 classpath 存在 MyService 时生效 public class MyStarterAutoConfiguration { private final MyStarterProperties properties; // 构造注入配置属性 public MyStarterAutoConfiguration(MyStarterProperties properties) { this.properties = properties; } // 注册自定义 Bean,依赖配置属性 @Bean @ConditionalOnMissingBean // 允许用户自定义 Bean 覆盖 public MyService myService() { MyService service = new MyService(); service.setName(properties.getName()); service.setTimeout(properties.getTimeout()); return service; } } -
创建
spring.factories文件:在src/main/resources/META-INF/下创建该文件,指定自动配置类:org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.my.starter.MyStarterAutoConfiguration
步骤 4:使用自定义 Starter
其他项目引入 Starter 依赖后,即可直接注入 MyService,并通过 application.yml 配置 my.starter.name 等属性。
4. 如何禁用某个自动配置类?
有 2 种常用方式:
-
通过
@SpringBootApplication的exclude属性(启动类上):// 禁用 DataSourceAutoConfiguration(避免自动配置数据源) @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } -
通过外部配置
spring.autoconfigure.exclude(application.yml中):spring: autoconfigure: exclude: - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration - org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
三、配置相关
1. Spring Boot 的配置文件有哪些格式?优先级如何?
Spring Boot 支持 3 种配置文件格式,用于外部化配置(如端口、数据库连接):
application.properties:键值对格式(server.port=8080),兼容性好。application.yml:YAML 格式(缩进敏感,server: port: 8080),结构清晰,推荐使用。application.yaml:与application.yml完全一致,只是文件名不同。
配置文件优先级(从高到低,高优先级覆盖低优先级):
- 命令行参数(如
java -jar app.jar --server.port=8081)。 - 系统环境变量(如 OS 的
SPRING_DATASOURCE_URL)。 - 项目内配置文件:
file:./config/(项目根目录下的 config 文件夹)。file:./(项目根目录)。classpath:/config/(resources 下的 config 文件夹)。classpath:/(resources 根目录,默认位置)。
- 特殊配置(如
application-{profile}.yml,需结合spring.profiles.active激活)。
2. @Value 和 @ConfigurationProperties 的区别?如何选择?
两者都用于注入外部配置,但适用场景不同:
| 对比维度 | @Value |
@ConfigurationProperties |
|---|---|---|
| 注入方式 | 单个属性注入(需逐个标注)。 | 批量注入(绑定整个前缀的配置,如 spring.datasource)。 |
| 支持类型 | 基本类型(String、int 等)、SpEL 表达式。 | 支持复杂类型(对象、集合、Map 等),自动类型转换。 |
| 校验 | 不支持 JSR-380 校验(如 @NotNull)。 |
支持 JSR-380 校验(需配合 @Validated)。 |
| 松散绑定 | 不支持(如 myName 无法绑定 my-name)。 |
支持松散绑定(myName、my-name、MY_NAME 均可绑定)。 |
| 适用场景 | 简单配置(如单个端口、单个密钥)。 | 复杂配置(如数据源、Redis、自定义 Starter 配置)。 |
选择建议:
- 注入单个简单配置 → 使用
@Value(如@Value("${server.port}"))。 - 注入一组相关配置(如数据源的 url、username、password)→ 使用
@ConfigurationProperties。
3. 什么是 Spring Boot Profile?如何使用?
Profile 是 Spring Boot 的“环境隔离”功能,用于区分开发(dev)、测试(test)、生产(prod) 等环境的配置,避免配置文件冲突。
使用步骤:
-
创建环境专属配置文件:
- 开发环境:
application-dev.yml。 - 测试环境:
application-test.yml。 - 生产环境:
application-prod.yml。
(核心配置仍可放在application.yml中,环境专属配置覆盖核心配置)。
- 开发环境:
-
激活 Profile:
- 方式 1:配置文件激活(
application.yml中):spring: profiles: active: dev # 激活开发环境 - 方式 2:命令行激活(优先级更高):
java -jar app.jar --spring.profiles.active=prod - 方式 3:代码激活(启动类中):
public static void main(String[] args) { SpringApplication app = new SpringApplication(Application.class); app.setAdditionalProfiles("test"); // 激活测试环境 app.run(args); }
- 方式 1:配置文件激活(
-
Profile 注解:通过
@Profile控制 Bean/配置类在指定环境生效:@Configuration @Profile("prod") // 仅生产环境生效 public class ProdDataSourceConfig { // 生产环境数据源配置 }
四、Web 开发相关
1. Spring Boot 如何整合 Spring MVC?需要手动配置吗?
无需手动配置,引入 spring-boot-starter-web 依赖后,Spring Boot 会自动完成 Spring MVC 的核心配置:
- 自动配置
DispatcherServlet(前端控制器),并映射到/路径。 - 自动配置
CharacterEncodingFilter(解决中文乱码,默认 UTF-8)。 - 自动配置
ViewResolver(视图解析器,支持 JSP、Thymeleaf 等)。 - 自动配置静态资源映射(
classpath:/static/、classpath:/public/等目录下的资源可直接访问)。
如需自定义 Spring MVC 配置(如拦截器、视图解析器),有 2 种方式:
-
实现
WebMvcConfigurer接口(推荐):@Configuration public class WebMvcConfig implements WebMvcConfigurer { // 自定义拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/**") // 拦截所有路径 .excludePathPatterns("/login"); // 排除登录路径 } // 自定义视图解析器 @Override public void configureViewResolvers(ViewResolverRegistry registry) { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); registry.viewResolver(resolver); } } -
使用
@EnableWebMvc注解(谨慎使用):
标注@EnableWebMvc会完全覆盖 Spring Boot 的自动配置,需手动配置所有 Spring MVC 组件(如DispatcherServlet、静态资源映射),仅在需要完全自定义时使用。
2. Spring Boot 如何处理跨域(CORS)问题?
跨域是浏览器的安全限制(不同域名/端口/协议的请求被拦截),Spring Boot 有 3 种常用解决方案:
方案 1:通过 WebMvcConfigurer 全局配置
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 允许跨域的路径(所有路径)
.allowedOrigins("http://localhost:8081") // 允许的源(前端地址)
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的请求方法
.allowedHeaders("*") // 允许的请求头
.allowCredentials(true) // 是否允许携带 Cookie
.maxAge(3600); // 预检请求(OPTIONS)的缓存时间(秒)
}
}
方案 2:使用 @CrossOrigin 注解(局部配置)
在 Controller 或方法上标注,仅对当前 Controller/方法生效:
@RestController
@CrossOrigin(origins = "http://localhost:8081", maxAge = 3600)
public class UserController {
@GetMapping("/users")
public List<User> getUsers() {
// ...
}
}
方案 3:自定义 CORS 过滤器
@Component
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.setHeader("Access-Control-Allow-Origin", "http://localhost:8081");
res.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE");
res.setHeader("Access-Control-Allow-Headers", "*");
res.setHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(request, response);
}
}
3. Spring Boot 如何实现统一异常处理?
通过 @ControllerAdvice + @ExceptionHandler 实现全局统一异常处理,避免在每个 Controller 中重复捕获异常。
步骤:
-
创建全局异常处理类:
@ControllerAdvice // 全局Controller增强,捕获所有Controller的异常 @ResponseBody // 返回JSON格式响应(如用@RestControllerAdvice可省略) public class GlobalExceptionHandler { // 处理特定异常(如空指针异常) @ExceptionHandler(NullPointerException.class) public Result handleNullPointerException(NullPointerException e) { log.error("空指针异常:", e); return Result.fail(500, "服务器内部错误:空指针"); } // 处理自定义异常(如业务异常) @ExceptionHandler(BusinessException.class) public Result handleBusinessException(BusinessException e) { log.error("业务异常:", e); return Result.fail(e.getCode(), e.getMessage()); } // 处理所有未捕获的异常(兜底) @ExceptionHandler(Exception.class) public Result handleException(Exception e) { log.error("未知异常:", e); return Result.fail(500, "服务器内部错误,请联系管理员"); } } -
定义统一响应类(Result):
public class Result<T> { private int code; // 状态码(200=成功,500=失败) private String message; // 提示信息 private T data; // 响应数据 // 成功静态方法 public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(200); result.setMessage("success"); result.setData(data); return result; } // 失败静态方法 public static <T> Result<T> fail(int code, String message) { Result<T> result = new Result<>(); result.setCode(code); result.setMessage(message); return result; } // getter/setter }
五、数据访问相关
1. Spring Boot 如何整合 MyBatis?
MyBatis 是常用的 ORM 框架,Spring Boot 通过 mybatis-spring-boot-starter 简化整合:
步骤 1:引入依赖(pom.xml)
<dependencies>
<!-- MyBatis Starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.1</version> <!-- 需与Spring Boot版本兼容 -->
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 数据源(HikariCP,Spring Boot默认) -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
</dependencies>
步骤 2:配置数据源和 MyBatis(application.yml)
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC
username: root
password: 123456
type: com.zaxxer.hikari.HikariDataSource # 默认数据源,可省略
mybatis:
mapper-locations: classpath:mapper/**/*.xml # MyBatis映射文件路径
type-aliases-package: com.example.demo.entity # 实体类包路径(简化XML中的类名)
configuration:
map-underscore-to-camel-case: true # 开启下划线转驼峰(如user_name → userName)
步骤 3:编写 Mapper 接口
通过 @Mapper 注解或 @MapperScan 扫描 Mapper 接口:
// 方式1:在接口上标注@Mapper
@Mapper
public interface UserMapper {
List<User> selectAll();
User selectById(Long id);
int insert(User user);
}
// 方式2:在启动类上标注@MapperScan(扫描所有Mapper接口,推荐)
@SpringBootApplication
@MapperScan("com.example.demo.mapper") // Mapper接口所在包
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
步骤 4:编写 MyBatis 映射文件(XML)
在 resources/mapper/ 下创建 UserMapper.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
<select id="selectAll" resultType="User">
select id, user_name, age from user
</select>
<select id="selectById" parameterType="Long" resultType="User">
select id, user_name, age from user where id = #{id}
</select>
</mapper>
步骤 5:Service 层调用 Mapper
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<User> getAllUsers() {
return userMapper.selectAll();
}
}
2. Spring Boot 默认的数据源是什么?如何切换数据源(如 Druid)?
(1)默认数据源:HikariCP
Spring Boot 2.x 及以上版本默认使用 HikariCP 作为数据源,原因是它是目前性能最优的 JDBC 连接池(启动快、内存占用低、并发性能好)。无需额外引入依赖(spring-boot-starter-jdbc 或 mybatis-spring-boot-starter 已包含)。
(2)切换为 Druid 数据源
Druid 是阿里开源的数据源,支持监控、SQL 拦截、防注入等功能,切换步骤如下:
-
引入 Druid Starter 依赖:
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.16</version> </dependency> -
配置 Druid 数据源(application.yml):
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource # 指定数据源类型 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC username: root password: 123456 # Druid 特有配置 druid: initial-size: 5 # 初始化连接数 min-idle: 5 # 最小空闲连接数 max-active: 20 # 最大活跃连接数 max-wait: 60000 # 获取连接的最大等待时间(毫秒) time-between-eviction-runs-millis: 60000 # 检测间隔(毫秒) min-evictable-idle-time-millis: 300000 # 连接最小空闲时间(毫秒) validation-query: SELECT 1 FROM DUAL # 验证连接的SQL test-while-idle: true # 空闲时检测 test-on-borrow: false # 借出时检测(影响性能) test-on-return: false # 归还时检测(影响性能) pool-prepared-statements: true # 开启PSCache max-pool-prepared-statement-per-connection-size: 20 # PSCache大小 # 配置监控统计拦截的filters filters: stat,wall,log4j2 # stat=监控统计,wall=防SQL注入,log4j2=日志 # 监控页面配置 stat-view-servlet: enabled: true # 启用监控页面 url-pattern: /druid/* # 监控页面访问路径 login-username: admin # 监控页面登录用户名 login-password: 123456 # 监控页面登录密码 -
访问 Druid 监控页面:启动项目后,访问
http://localhost:8080/druid,输入配置的用户名密码即可查看数据源监控、SQL 执行情况等。
六、生产级特性
1. Spring Boot Actuator 是什么?如何使用?
Actuator 是 Spring Boot 提供的“生产级监控工具”,通过 HTTP 端点暴露应用的健康状态、 metrics 指标、环境配置、Bean 信息等,帮助运维人员监控应用。
使用步骤:
-
引入 Actuator 依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> -
配置 Actuator 端点(application.yml):
management: endpoints: web: exposure: include: "*" # 暴露所有Web端点(生产环境建议仅暴露必要端点,如health,info) exclude: env,beans # 排除敏感端点 endpoint: health: show-details: always # 显示健康状态详情(如数据库、Redis的健康状态) shutdown: enabled: true # 启用关闭端点(需谨慎,生产环境不建议开启) -
常用端点说明:
/actuator/health:应用健康状态(UP/DOWN,可扩展监控数据源、Redis等)。/actuator/info:应用信息(需配置info前缀的属性,如info.app.name=demo)。/actuator/metrics:应用 metrics 指标(如 JVM 内存、CPU 使用率、HTTP 请求数)。/actuator/env:环境配置(如系统变量、配置文件属性)。/actuator/beans:Spring 容器中所有 Bean 的信息。/actuator/shutdown:POST 请求关闭应用(需开启shutdown.enabled=true)。
2. Spring Boot 如何实现热部署?
热部署指“修改代码后无需重启应用,即可生效”,提高开发效率,常用方案是 Spring Boot DevTools。
步骤:
-
引入 DevTools 依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> <!-- 防止依赖传递 --> </dependency> -
配置 IDE(以 IDEA 为例):
- 开启自动编译:
File → Settings → Build, Execution, Deployment → Compiler → 勾选 Build project automatically。 - 开启运行时自动编译:按
Ctrl + Shift + Alt + /→ 选择Registry→ 勾选compiler.automake.allow.when.app.running。
- 开启自动编译:
-
原理:
DevTools 采用“双类加载器”机制:- Base ClassLoader:加载不变的类(如第三方 Jar 包)。
- Restart ClassLoader:加载项目自定义的类(如 Controller、Service)。
当代码修改时,仅重启Restart ClassLoader,无需重启整个应用,实现热部署。
注意:
- DevTools 仅用于开发环境,生产环境需排除(可通过 Profile 控制)。
- 部分修改(如配置文件、静态资源)无需重启,DevTools 会自动刷新。
七、性能优化
1. Spring Boot 应用的常见性能优化手段有哪些?
从“配置、代码、依赖、JVM”等维度优化,核心手段如下:
-
优化数据源:
- 使用高性能数据源(如 HikariCP,默认),避免使用低性能的 C3P0。
- 合理配置连接池参数(
initial-size、max-active、max-wait等),避免连接泄露或连接数不足。
-
优化嵌入式容器:
- 切换为 Undertow 容器(比 Tomcat 性能更优,支持异步 IO):
<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> <!-- 引入Undertow --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency> - 配置容器线程池(如 Tomcat 的
max-threads、min-spare-threads)。
- 切换为 Undertow 容器(比 Tomcat 性能更优,支持异步 IO):
-
优化 JVM 参数:
- 合理设置堆内存(
-Xms、-Xmx,建议相同值,避免内存波动)。 - 启用 G1 垃圾收集器(
-XX:+UseG1GC,适合大内存应用)。 - 示例:
java -jar -Xms2g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 app.jar。
- 合理设置堆内存(
-
代码与框架优化:
- 避免频繁创建对象(如使用线程池、连接池、对象池)。
- 使用缓存(如 Redis、Caffeine)减少数据库查询。
- 减少 HTTP 请求(如接口聚合、压缩响应数据)。
- 避免循环依赖(Spring 虽支持,但影响性能和代码可读性)。
-
依赖优化:
- 排除无用依赖(如
spring-boot-starter-web中排除 Tomcat 切换为 Undertow)。 - 使用最新稳定版本的依赖(修复已知性能问题)。
- 排除无用依赖(如
-
开启缓存与压缩:
- 开启 HTTP 响应压缩(
server.compression.enabled=true)。 - 静态资源使用 CDN 或缓存(设置
Cache-Control头)。
- 开启 HTTP 响应压缩(
2. 如何解决 Spring Boot 应用的循环依赖问题?
循环依赖指“两个或多个 Bean 互相依赖”(如 A 依赖 B,B 依赖 A),Spring 默认支持基于 setter 注入或字段注入的循环依赖,但不支持基于构造器注入的循环依赖(会抛 BeanCurrentlyInCreationException)。
(1)循环依赖的产生场景
// 场景1:构造器注入循环依赖(Spring 无法解决)
@Service
public class AService {
private BService bService;
// 构造器注入 BService
@Autowired
public AService(BService bService) {
this.bService = bService;
}
}
@Service
public class BService {
private AService aService;
// 构造器注入 AService
@Autowired
public BService(AService aService) {
this.aService = aService;
}
}
(2)解决方案
-
改用 setter 注入或字段注入(推荐):
@Service public class AService { private BService bService; // setter注入 @Autowired public void setBService(BService bService) { this.bService = bService; } } @Service public class BService { // 字段注入 @Autowired private AService aService; } -
使用
@Lazy注解(延迟加载):
对其中一个 Bean 延迟初始化,打破循环依赖:@Service public class AService { private BService bService; // 对 BService 延迟加载 @Autowired public AService(@Lazy BService bService) { this.bService = bService; } } @Service public class BService { private AService aService; @Autowired public BService(AService aService) { this.aService = aService; } } -
重构代码(从根源解决):
提取循环依赖的公共逻辑到新的 Bean(如 CService),让 A 和 B 都依赖 C,而非互相依赖:// 提取公共逻辑到 CService @Service public class CService { // 公共方法 public void commonMethod() { ... } } @Service public class AService { private CService cService; @Autowired public AService(CService cService) { this.cService = cService; } } @Service public class BService { private CService cService; @Autowired public BService(CService cService) { this.cService = cService; } }
八、其他高频问题
1. Spring Boot 中的 Bean 作用域有哪些?
Spring Bean 的作用域决定了 Bean 的创建时机和生命周期,Spring Boot 支持 6 种作用域:
| 作用域 | 说明 | 适用场景 |
|---|---|---|
| singleton(默认) | 整个 Spring 容器中,Bean 仅创建一个实例,全局共享。 | 无状态 Bean(如 Service、Dao)。 |
| prototype | 每次请求 Bean(如 getBean() 或 @Autowired)时,都创建一个新实例。 |
有状态 Bean(如 Command、Request)。 |
| request | 每个 HTTP 请求创建一个新 Bean,请求结束后销毁。 | Web 应用中,绑定请求数据的 Bean。 |
| session | 每个 HTTP Session 创建一个新 Bean,Session 过期后销毁。 | Web 应用中,绑定用户会话的 Bean。 |
| application | 整个 Web 应用(ServletContext)中,Bean 仅创建一个实例。 | Web 应用中,全局共享的 Bean(如应用配置)。 |
| websocket | 每个 WebSocket 会话创建一个新 Bean,WebSocket 关闭后销毁。 | WebSocket 应用。 |
如何设置作用域?
通过 @Scope 注解:
@Service
@Scope("prototype") // 设置为 prototype 作用域
public class UserService {
// ...
}
2. Spring Boot 如何实现定时任务?
Spring Boot 通过 @EnableScheduling + @Scheduled 简化定时任务开发,无需集成 Quartz(简单场景)。
步骤:
-
启动类开启定时任务:
@SpringBootApplication @EnableScheduling // 开启定时任务支持 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } -
编写定时任务方法:
在 Bean 类(如 Service)中,用@Scheduled标注定时方法:@Service public class ScheduledService { // 1. 固定延迟执行(上一次任务结束后,延迟 5 秒执行下一次) @Scheduled(fixedDelay = 5000) public void fixedDelayTask() { System.out.println("固定延迟任务:" + new Date()); } // 2. 固定速率执行(上一次任务开始后,间隔 5 秒执行下一次,可能并发) @Scheduled(fixedRate = 5000) public void fixedRateTask() { System.out.println("固定速率任务:" + new Date()); } // 3. 初始延迟后执行(项目启动后延迟 3 秒,再固定速率执行) @Scheduled(initialDelay = 3000, fixedRate = 5000) public void initialDelayTask() { System.out.println("初始延迟任务:" + new Date()); } // 4. Cron 表达式(灵活配置,如每天 0 点执行) @Scheduled(cron = "0 0 0 * * ?") public void cronTask() { System.out.println("Cron 任务:" + new Date()); } }
Cron 表达式格式:
秒 分 时 日 月 周 [年](年可选),常用示例:
0 0 0 * * ?:每天 0 点执行。0 0/30 * * * ?:每 30 分钟执行一次。0 15 10 ? * MON-FRI:每周一到周五 10:15 执行。
3. Spring Boot 如何实现异步任务?
通过 @EnableAsync + @Async 实现异步任务,避免同步任务阻塞主线程(如发送短信、邮件等耗时操作)。
步骤:
-
启动类开启异步支持:
@SpringBootApplication @EnableAsync // 开启异步任务支持 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } -
编写异步任务方法:
在 Bean 类的方法上标注@Async,该方法会在独立线程中执行:@Service public class AsyncService { // 异步方法(无返回值) @Async public void sendSms(String phone) { try { Thread.sleep(3000); // 模拟耗时操作(发送短信) } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("短信发送成功:" + phone); } // 异步方法(有返回值,需返回 Future) @Async public CompletableFuture<String> sendEmail(String email) { try { Thread.sleep(2000); // 模拟耗时操作(发送邮件) } catch (InterruptedException e) { e.printStackTrace(); } String result = "邮件发送成功:" + email; return CompletableFuture.completedFuture(result); } } -
调用异步方法:
@RestController public class AsyncController { @Autowired private AsyncService asyncService; @GetMapping("/send") public String send() { long start = System.currentTimeMillis(); // 调用异步方法(不阻塞主线程) asyncService.sendSms("13800138000"); CompletableFuture<String> emailFuture = asyncService.sendEmail("test@example.com"); // 等待异步方法结果(可选) try { String emailResult = emailFuture.get(); // 阻塞等待结果 System.out.println(emailResult); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); return "总耗时:" + (end - start) + "ms"; // 耗时约 2000ms(取最长的异步任务时间) } }
注意:
@Async标注的方法必须在不同的 Bean 中调用(如果在同一个 Bean 中调用,异步不生效,因为 Spring AOP 基于代理,同类方法调用不会走代理)。- 可自定义线程池(实现
AsyncConfigurer接口),避免使用默认线程池(SimpleAsyncTaskExecutor,每次创建新线程,性能差)。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐

所有评论(0)