Spring Boot 框架深入解析与面试实战
📋 目录
- 1. Spring Boot 核心概念
- 2. 自动配置原理深入解析
- 3. Starter依赖机制详解
- 4. Spring Boot配置体系
- 5. 嵌入式服务器详解
- 6. Spring Boot生命周期管理
- 7. Spring Boot数据访问
- 8. Spring Boot监控和管理
- 9. Spring Boot测试体系
- 10. Spring Boot性能优化
- 11. Spring Boot微服务实践
- 12. 经典面试题与实战案例
1. Spring Boot 核心概念
1.1 什么是Spring Boot?
Spring Boot是基于Spring框架的开发框架,它简化了Spring应用的创建和部署过程。
核心理念
- 约定优于配置:提供大量的默认配置,减少开发者的配置工作
- 开箱即用:提供一系列"starter"依赖,快速集成常用功能
- 独立运行:内嵌Web服务器,应用可以独立运行
- 生产就绪:提供健康检查、指标收集等生产级特性
与Spring Framework的关系
Spring Framework: 核心容器(IoC/AOP)
↓
Spring Boot: 基于Spring Framework的自动化开发框架
1.2 Spring Boot核心特性
自动配置(Auto Configuration)
Spring Boot会根据类路径中的依赖自动配置Spring应用上下文。
示例:当检测到spring-boot-starter-web时,会自动配置:
- DispatcherServlet
- 内嵌Tomcat服务器
- Jackson消息转换器
- Spring MVC相关组件
起步依赖(Starter Dependencies)
提供了一组便利的依赖描述符,一站式解决依赖管理。
常用Starter:
<!-- Web应用开发 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 数据访问 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- 安全框架 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
嵌入式服务器
内嵌Tomcat、Jetty或Undertow服务器,无需部署WAR文件。
生产就绪特性
- 健康检查端点
- 应用指标收集
- 外部化配置
- 日志管理
1.3 Spring Boot项目结构
my-spring-boot-app/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/myapp/
│ │ │ ├── MyApplication.java # 启动类
│ │ │ ├── controller/ # 控制器层
│ │ │ ├── service/ # 业务层
│ │ │ ├── repository/ # 数据访问层
│ │ │ ├── config/ # 配置类
│ │ │ └── model/ # 实体类
│ │ └── resources/
│ │ ├── application.yml # 主配置文件
│ │ ├── application-dev.yml # 开发环境配置
│ │ ├── application-prod.yml # 生产环境配置
│ │ ├── static/ # 静态资源
│ │ ├── templates/ # 模板文件
│ │ └── logback-spring.xml # 日志配置
│ └── test/ # 测试代码
├── pom.xml # Maven配置
└── README.md # 项目说明
1.4 核心注解解析
@SpringBootApplication
这是一个组合注解,包含:
@SpringBootConfiguration // 标识这是一个配置类
@EnableAutoConfiguration // 启用自动配置
@ComponentScan // 组件扫描
@ConfigurationProperties
用于将外部配置绑定到Java对象:
@ConfigurationProperties(prefix = "app.datasource")
public class DataSourceProperties {
private String url;
private String username;
private String password;
// getters and setters
}
@Conditional
条件化配置,只在满足特定条件时才创建Bean:
@Configuration
@ConditionalOnClass(DataSource.class) // 当DataSource类存在时
@ConditionalOnMissingBean(DataSource.class) // 当没有自定义DataSource时
public class DataSourceAutoConfiguration {
// 自动配置逻辑
}
2. 自动配置原理深入解析
2.1 自动配置机制概述
Spring Boot的自动配置是其最核心的特性,它通过智能的类路径分析来配置Spring应用。
工作流程
1. @EnableAutoConfiguration启用自动配置
2. SpringFactoriesLoader加载META-INF/spring.factories
3. 根据条件注解进行Bean条件化配置
4. 应用用户自定义配置覆盖自动配置
2.2 spring.factories机制
基本原理
spring.factories文件位于各个starter的META-INF/目录下,定义了自动配置类:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
加载机制
public final class SpringFactoriesLoader {
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
// 1. 从缓存中获取
// 2. 加载所有jar包中的META-INF/spring.factories
// 3. 解析配置类并实例化
}
}
2.3 条件注解详解
条件注解是自动配置的核心控制机制,决定了哪些配置会被应用。
核心条件注解
// 类级别条件
@ConditionalOnClass(DataSource.class) // 类路径中存在指定类
@ConditionalOnMissingClass(DataSource.class) // 类路径中不存在指定类
// Bean级别条件
@ConditionalOnBean(DataSource.class) // 容器中存在指定Bean
@ConditionalOnMissingBean(DataSource.class) // 容器中不存在指定Bean
// 属性条件
@ConditionalOnProperty(name = "spring.datasource.url", matchIfMissing = false)
// 资源条件
@ConditionalOnResource(resources = "classpath:application.properties")
// Web应用条件
@ConditionalOnWebApplication(type = Type.SERVLET) // Servlet Web应用
@ConditionalOnNotWebApplication // 非Web应用
// 表达式条件
@ConditionalOnExpression("${spring.profiles.active} != 'test'")
2.4 自动配置优先级和覆盖
配置优先级
1. 用户显式配置的Bean
2. 用户自定义的@Configuration类
3. 自动配置类
4. 默认配置
覆盖机制
// 用户可以通过以下方式覆盖自动配置:
// 1. 提供自定义Bean
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
// 2. 使用@Primary注解
@Bean
@Primary
public DataSource primaryDataSource() {
return new HikariDataSource();
}
// 3. 排除自动配置
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class MyApplication {
}
2.5 自定义自动配置
创建自定义Starter
// 1. 创建自动配置类
@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService(MyProperties properties) {
return new MyServiceImpl(properties);
}
}
// 2. 创建配置属性类
@ConfigurationProperties(prefix = "my.service")
public class MyProperties {
private String name = "default";
private int timeout = 3000;
// getters and setters
}
// 3. 创建spring.factories文件
# META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfigure.MyAutoConfiguration
2.6 自动配置调试
启用调试模式
# application.yml
debug: true # 启用自动配置报告
查看自动配置报告
启动时会输出详细的自动配置报告,显示:
- Positive matches:已应用的自动配置
- Negative matches:未应用的自动配置及其原因
使用条件评估报告
@Autowired
private ConditionEvaluationReport conditionEvaluationReport;
public void printAutoConfigurationReport() {
Map<String, ConditionAndOutcome> unconditionalAndOutcomes =
conditionEvaluationReport.getConditionAndOutcomesBySource();
// 分析自动配置条件
}
3. Starter依赖机制详解
3.1 Starter机制概述
Starter是Spring Boot提供的便利依赖描述符,它包含了一组相关的依赖。
设计理念
- 一站式依赖管理:一个Starter包含功能所需的所有依赖
- 传递性依赖:自动引入所需的第三方库
- 版本兼容性:确保所有依赖版本兼容
- 最小化配置:提供合理的默认配置
3.2 Spring Boot官方Starter分类
Web开发类
<!-- Web应用开发 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- WebFlux响应式Web开发 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- WebSocket支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
数据访问类
<!-- JDBC数据访问 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- JPA数据访问 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MongoDB数据访问 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- Redis缓存 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
消息队列类
<!-- RabbitMQ -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- Apache Kafka -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-kafka</artifactId>
</dependency>
安全和监控类
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Actuator监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
3.3 Starter内部结构分析
典型Starter的依赖构成
以spring-boot-starter-web为例:
<dependencies>
<!-- 核心Web依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- JSON处理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<!-- 内嵌Tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<!-- Spring MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
</dependencies>
3.4 自定义Starter开发
项目结构
my-spring-boot-starter/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/starter/
│ │ │ ├── MyAutoConfiguration.java # 自动配置类
│ │ │ ├── MyService.java # 核心服务接口
│ │ │ ├── MyServiceImpl.java # 服务实现
│ │ │ └── MyProperties.java # 配置属性类
│ │ └── resources/
│ │ └── META-INF/
│ │ ├── spring.factories # 自动配置声明
│ │ └── additional-spring-configuration-metadata.json # 配置元数据
└── pom.xml
核心组件实现
1. 配置属性类
@ConfigurationProperties(prefix = "example.service")
public class MyProperties {
private String name = "default";
private boolean enabled = true;
private int timeout = 3000;
private List<String> features = new ArrayList<>();
// getters and setters
}
2. 核心服务接口
public interface MyService {
String process(String input);
boolean isEnabled();
}
3. 服务实现
public class MyServiceImpl implements MyService {
private final MyProperties properties;
public MyServiceImpl(MyProperties properties) {
this.properties = properties;
}
@Override
public String process(String input) {
if (!properties.isEnabled()) {
throw new IllegalStateException("Service is disabled");
}
return input + " processed by " + properties.getName();
}
@Override
public boolean isEnabled() {
return properties.isEnabled();
}
}
4. 自动配置类
@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService(MyProperties properties) {
return new MyServiceImpl(properties);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "example.service", name = "enabled", havingValue = "true")
public MyServiceRunner myServiceRunner(MyService myService) {
return new MyServiceRunner(myService);
}
}
5. spring.factories配置
# META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.starter.MyAutoConfiguration
3.5 Starter最佳实践
命名规范
- 官方Starter:
spring-boot-starter-* - 第三方Starter:
*-spring-boot-starter
依赖管理
<!-- 在自定义Starter中,不指定版本 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<optional>true</optional> <!-- 标记为可选依赖 -->
</dependency>
</dependencies>
<!-- 在主应用的pom.xml中管理版本 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
配置元数据
// additional-spring-configuration-metadata.json
{
"properties": [
{
"name": "example.service.name",
"type": "java.lang.String",
"defaultValue": "default",
"description": "The name of the service."
},
{
"name": "example.service.enabled",
"type": "java.lang.Boolean",
"defaultValue": true,
"description": "Whether the service is enabled."
}
],
"hints": [
{
"name": "example.service.features",
"values": [
{
"value": "feature1",
"description": "Enable feature 1"
},
{
"value": "feature2",
"description": "Enable feature 2"
}
]
}
]
}
4. Spring Boot配置体系
4.1 配置文件类型和优先级
Spring Boot支持多种配置文件格式,具有明确的加载优先级。
配置文件格式
# application.yml
server:
port: 8080
servlet:
context-path: /api
# application.properties
server.port=8080
server.servlet.context-path=/api
配置文件优先级(从高到低)
1. 命令行参数
2. Java系统属性
3. 操作系统环境变量
4. 外部配置文件
- config/application.properties
- application.properties
- config/application.yml
- application.yml
5. @PropertySource注解
6. 默认属性
4.2 多环境配置管理
Profile机制
# application.yml
spring:
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev} # 默认激活dev环境
---
spring:
config:
activate:
on-profile: dev
server:
port: 8080
logging:
level:
root: DEBUG
---
spring:
config:
activate:
on-profile: prod
server:
port: 80
logging:
level:
root: INFO
多文件配置
src/main/resources/
├── application.yml # 通用配置
├── application-dev.yml # 开发环境
├── application-test.yml # 测试环境
├── application-prod.yml # 生产环境
└── application-local.yml # 本地环境
4.3 配置属性绑定
@ConfigurationProperties
@ConfigurationProperties(prefix = "app.datasource")
public class DataSourceProperties {
private String url;
private String username;
private String password;
private int maxConnections = 10;
private boolean autoCommit = true;
// 嵌套配置
private final Pool pool = new Pool();
public static class Pool {
private int maxSize = 30;
private int minIdle = 5;
private long timeout = 30000;
// getters and setters
}
// getters and setters
}
// 配置文件
app:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: password
max-connections: 20
auto-commit: false
pool:
max-size: 50
min-idle: 10
timeout: 60000
@Value注解
@Component
public class AppConfig {
@Value("${app.name:DefaultApp}") // 提供默认值
private String appName;
@Value("${app.version}")
private String version;
@Value("#{systemProperties['java.home']}") # SpEL表达式
private String javaHome;
@Value("${app.features}") # 绑定到集合
private List<String> features;
}
类型安全的配置验证
@ConfigurationProperties(prefix = "app")
@Validated // 启用验证
public class AppProperties {
@NotNull
@Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_-]*$")
private String name;
@Min(1024)
@Max(65535)
private int port;
@Valid
private final Database database = new Database();
public static class Database {
@NotBlank
private String url;
@Email // 邮箱格式验证
private String adminEmail;
// getters and setters
}
// getters and setters
}
4.4 配置热更新
Spring Cloud Config
# bootstrap.yml
spring:
application:
name: my-service
cloud:
config:
uri: http://config-server:8888
fail-fast: true
retry:
initial-interval: 1000
max-attempts: 6
@RefreshScope
@RestController
@RefreshScope // 支持配置热更新
public class ConfigController {
@Value("${app.message:Hello}")
private String message;
@GetMapping("/message")
public String getMessage() {
return message;
}
}
4.5 配置加密和解密
Jasypt加密配置
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
app:
datasource:
password: ENC(encrypted_password_here) # 加密的密码
jasypt:
encryptor:
password: ${JASYPT_PASSWORD} # 加密密钥
algorithm: PBEWithMD5AndDES
加密工具类
@Component
public class ConfigEncryptor {
private final StringEncryptor encryptor;
public ConfigEncryptor(StringEncryptor encryptor) {
this.encryptor = encryptor;
}
public String encrypt(String plainText) {
return "ENC(" + encryptor.encrypt(plainText) + ")";
}
public String decrypt(String encryptedText) {
if (encryptedText.startsWith("ENC(") && encryptedText.endsWith(")")) {
String content = encryptedText.substring(4, encryptedText.length() - 1);
return encryptor.decrypt(content);
}
return encryptedText;
}
}
5. 嵌入式服务器详解
5.1 支持的嵌入式服务器
Spring Boot支持三种主要的嵌入式服务器:
Tomcat(默认)
<!-- 引入Web Starter默认包含Tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Jetty
<!-- 排除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-jetty</artifactId>
</dependency>
Undertow
<!-- 排除Tomcat,引入Undertow -->
<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>
5.2 服务器配置详解
基础配置
server:
port: 8080 # 服务器端口
servlet:
context-path: /api # 应用上下文路径
encoding:
charset: UTF-8 # 字符编码
enabled: true
force: true
# Tomcat特定配置
tomcat:
max-threads: 200 # 最大线程数
min-spare-threads: 10 # 最小空闲线程数
max-connections: 8192 # 最大连接数
accept-count: 100 # 等待队列长度
connection-timeout: 20000 # 连接超时时间(毫秒)
max-http-post-size: 2MB # 最大POST请求大小
basedir: ./tmp # Tomcat基础目录
# Undertow特定配置
undertow:
max-http-post-size: 2MB
buffer-size: 1024
direct-buffers: true
# Jetty特定配置
jetty:
max-http-post-size: 2MB
max-threads: 200
min-threads: 8
高级配置
@Configuration
public class ServerConfig {
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> tomcatCustomizer() {
return factory -> {
// 自定义Tomcat配置
factory.addConnectorCustomizers(connector -> {
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
protocol.setMaxKeepAliveRequests(100);
protocol.setKeepAliveTimeout(30000);
});
// 添加额外的阀门
factory.addContextValves(new AccessLogValve());
};
}
@Bean
public WebServerFactoryCustomizer<UndertowServletWebServerFactory> undertowCustomizer() {
return factory -> {
factory.setIoThreads(Runtime.getRuntime().availableProcessors() * 2);
factory.setWorkerThreads(200);
factory.setBufferSize(1024);
factory.setBuffersPerRegion(1024);
};
}
}
5.3 SSL/HTTPS配置
配置HTTPS
server:
port: 8443 # HTTPS端口
ssl:
enabled: true # 启用SSL
key-store: classpath:keystore.p12 # 密钥库路径
key-store-password: password # 密钥库密码
key-store-type: PKCS12 # 密钥库类型
key-alias: tomcat # 密钥别名
trust-store: classpath:truststore.p12 # 信任库
trust-store-password: password
client-auth: need # 客户端认证
HTTP重定向到HTTPS
@Configuration
public class HttpsConfig {
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(httpConnector());
return tomcat;
}
private Connector httpConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
connector.setPort(8080);
connector.setSecure(false);
connector.setRedirectPort(8443);
return connector;
}
}
5.4 服务器性能优化
Tomcat优化配置
server:
tomcat:
# 线程池配置
max-threads: 300
min-spare-threads: 20
max-connections: 10000
accept-count: 200
# 连接器优化
connection-timeout: 20000
max-http-form-post-size: 10MB
max-swallow-size: 2MB
# JVM优化
basedir: /var/tmp/tomcat
accesslog:
enabled: true
pattern: "%h %l %u %t \"%r\" %s %b %D %{User-Agent}i"
Undertow性能配置
@Configuration
public class UndertowPerformanceConfig {
@Bean
public UndertowServletWebServerFactory undertowFactory() {
UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
// 设置IO线程数(通常为CPU核心数的2倍)
factory.setIoThreads(Runtime.getRuntime().availableProcessors() * 2);
// 设置工作线程数
factory.setWorkerThreads(200);
// 配置缓冲区
factory.setBufferSize(1024);
factory.setBuffersPerRegion(1024);
factory.setDirectBuffers(true);
// 配置连接器
factory.addBuilderCustomizers(builder -> {
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true);
builder.setServerOption(UndertowOptions.MAX_HEADER_SIZE, 8192);
builder.setServerOption(UndertowOptions.MAX_ENTITY_SIZE, 10 * 1024 * 1024);
});
return factory;
}
}
5.5 服务器监控和诊断
Actuator监控端点
management:
endpoints:
web:
exposure:
include: health,info,metrics,httptrace,env,configprops
endpoint:
health:
show-details: always
server:
port: 8081 # 管理端口与应用端口分离
自定义健康检查
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
private final DataSource dataSource;
@Override
public Health health() {
try (Connection connection = dataSource.getConnection()) {
if (connection.isValid(1)) {
return Health.up()
.withDetail("database", "MySQL")
.withDetail("validationQuery", "SELECT 1")
.build();
}
return Health.down()
.withDetail("error", "Database connection failed")
.build();
} catch (SQLException e) {
return Health.down()
.withDetail("error", e.getMessage())
.build();
}
}
}
6. Spring Boot生命周期管理
6.1 应用启动流程
Spring Boot应用的生命周期从创建SpringApplication对象开始,到应用完全启动结束。
完整启动流程
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
// 1. 创建SpringApplication对象
SpringApplication app = new SpringApplication(MyApplication.class);
// 2. 自定义配置(可选)
app.setBannerMode(Banner.Mode.OFF);
app.setWebApplicationType(WebApplicationType.SERVLET);
// 3. 启动应用
app.run(args);
}
}
启动流程详细步骤
1. 创建SpringApplication实例
├─ 推断应用类型(Web应用/普通应用)
├─ 加载ApplicationContextInitializer
├─ 加载ApplicationListener
└─ 推断主启动类
2. 准备环境(Environment)
├─ 加载配置文件
├─ 激活Profile
└─ 设置属性源
3. 创建ApplicationContext
├─ 根据应用类型选择上下文类型
├─ 初始化上下文
└─ 加载Bean定义
4. 准备上下文
├─ 打印Banner
├─ 加载源文件
└─ 应用初始化器
5. 刷新上下文
├─ 创建BeanFactory
├─ 执行BeanFactoryPostProcessor
├─ 注册BeanPostProcessor
├─ 初始化MessageSource
├─ 初始化事件广播器
├─ 初始化单例Bean
└─ 完成刷新
6. 应用启动完成
├─ 调用ApplicationRunner
├─ 调用CommandLineRunner
└─ 发布应用启动事件
6.2 启动事件和监听器
应用启动事件顺序
@Component
public class ApplicationStartupListener {
@EventListener
public void handleApplicationStartingEvent(ApplicationStartingEvent event) {
System.out.println("1. 应用开始启动");
}
@EventListener
public void handleApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
System.out.println("2. 环境准备完成");
}
@EventListener
public void handleApplicationContextInitializedEvent(ApplicationContextInitializedEvent event) {
System.out.println("3. 上下文初始化完成");
}
@EventListener
public void handleApplicationPreparedEvent(ApplicationPreparedEvent event) {
System.out.println("4. 上下文准备完成");
}
@EventListener
public void handleApplicationStartedEvent(ApplicationStartedEvent event) {
System.out.println("5. 应用启动完成");
}
@EventListener
public void handleApplicationReadyEvent(ApplicationReadyEvent event) {
System.out.println("6. 应用就绪");
}
}
自定义事件监听器
// 方式1:通过注解
@EventListener
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomEventListener {
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("上下文刷新完成");
}
public void onApplicationEvent(ContextClosedEvent event) {
System.out.println("上下文关闭");
}
}
// 方式2:实现ApplicationListener接口
@Component
public class CustomApplicationListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
System.out.println("应用准备就绪");
}
}
6.3 优雅关闭机制
优雅关闭配置
# application.yml
server:
shutdown: graceful # 启用优雅关闭
spring:
lifecycle:
timeout-per-shutdown-phase: 30s # 关闭超时时间
management:
endpoint:
shutdown:
enabled: true # 启用关闭端点
生命周期组件管理
@Component
public class GracefulShutdownManager implements SmartLifecycle {
private volatile boolean running = false;
private final ExecutorService executorService = Executors.newFixedThreadPool(10);
@Override
public void start() {
running = true;
System.out.println("优雅关闭管理器启动");
}
@Override
public void stop() {
System.out.println("开始优雅关闭...");
running = false;
// 关闭线程池
executorService.shutdown();
try {
if (!executorService.awaitTermination(20, TimeUnit.SECONDS)) {
executorService.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
Thread.currentThread().interrupt();
}
System.out.println("优雅关闭完成");
}
@Override
public boolean isRunning() {
return running;
}
@Override
public int getPhase() {
return 0; // 优先级
}
}
6.4 启动性能监控
启动时间分析
@Component
public class StartupPerformanceAnalyzer {
private final Map<String, Long> phaseTimestamps = new ConcurrentHashMap<>();
private long applicationStartTime;
@EventListener
public void onApplicationStarting(ApplicationStartingEvent event) {
applicationStartTime = System.currentTimeMillis();
phaseTimestamps.put("starting", applicationStartTime);
System.out.println("应用开始启动: " + applicationStartTime);
}
@EventListener
public void onApplicationReady(ApplicationReadyEvent event) {
long readyTime = System.currentTimeMillis();
phaseTimestamps.put("ready", readyTime);
long totalTime = readyTime - applicationStartTime;
System.out.println("应用启动完成,总耗时: " + totalTime + "ms");
analyzeStartupPerformance();
}
private void analyzeStartupPerformance() {
System.out.println("\n=== 启动性能分析 ===");
long totalTime = phaseTimestamps.get("ready") - applicationStartTime;
// 分析各阶段耗时
phaseTimestamps.forEach((phase, timestamp) -> {
long phaseTime = timestamp - applicationStartTime;
System.out.println(phase + ": " + phaseTime + "ms");
});
// 性能建议
if (totalTime > 10000) {
System.out.println("⚠️ 启动时间超过10秒,建议优化:");
System.out.println(" - 检查数据库连接配置");
System.out.println(" - 优化Bean初始化逻辑");
System.out.println(" - 考虑延迟初始化非必要Bean");
}
}
}
7. Spring Boot数据访问
7.1 数据源自动配置
Spring Boot提供了强大的数据源自动配置功能,支持多种数据库连接池。
内置连接池支持
Spring Boot 2.x默认使用HikariCP作为连接池,也支持Tomcat JDBC、DBCP2等。
# application.yml - 数据源配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
# HikariCP连接池配置
hikari:
maximum-pool-size: 20 # 最大连接数
minimum-idle: 5 # 最小空闲连接数
idle-timeout: 300000 # 空闲超时时间(5分钟)
max-lifetime: 1800000 # 连接最大存活时间(30分钟)
connection-timeout: 20000 # 连接超时时间(20秒)
leak-detection-threshold: 60000 # 连接泄漏检测阈值(60秒)
# 连接池名称
pool-name: MyHikariCP
# 连接测试
connection-test-query: SELECT 1
validation-timeout: 3000
多数据源配置
@Configuration
public class DataSourceConfig {
// 主数据源
@Primary
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
// 从数据源
@Bean(name = "secondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
// 主数据源JPA配置
@Primary
@Bean(name = "primaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
EntityManagerFactoryBuilder builder,
@Qualifier("primaryDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("com.example.primary.entity")
.persistenceUnit("primary")
.build();
}
// 从数据源JPA配置
@Bean(name = "secondaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(
EntityManagerFactoryBuilder builder,
@Qualifier("secondaryDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("com.example.secondary.entity")
.persistenceUnit("secondary")
.build();
}
}
# 多数据源配置
spring:
datasource:
primary:
url: jdbc:mysql://localhost:3306/primary_db
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 15
secondary:
url: jdbc:mysql://localhost:3306/secondary_db
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 10
7.2 JPA数据访问
实体类定义
@Entity
@Table(name = "users")
@EntityListeners(AuditingEntityListener.class)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 50)
private String username;
@Column(nullable = false)
private String password;
@Column(unique = true, length = 100)
private String email;
@Enumerated(EnumType.STRING)
private UserStatus status;
@Temporal(TemporalType.TIMESTAMP)
@CreatedDate
private Date createdAt;
@Temporal(TemporalType.TIMESTAMP)
@LastModifiedDate
private Date updatedAt;
// 关联关系
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Order> orders;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(
name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<Role> roles;
// 构造函数、getters、setters
}
Repository接口
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 基本查询方法
Optional<User> findByUsername(String username);
List<User> findByEmail(String email);
List<User> findByStatus(UserStatus status);
// 分页查询
Page<User> findByStatusOrderByCreatedAtDesc(UserStatus status, Pageable pageable);
// 复合条件查询
List<User> findByUsernameContainingAndStatus(String username, UserStatus status);
// 自定义JPQL查询
@Query("SELECT u FROM User u WHERE u.email = ?1 AND u.status = ?2")
Optional<User> findByEmailAndStatus(String email, UserStatus status);
// 原生SQL查询
@Query(value = "SELECT COUNT(*) FROM users WHERE created_at >= ?1", nativeQuery = true)
long countUsersCreatedSince(Date startDate);
// 更新操作
@Modifying
@Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
int updateUserStatus(@Param("id") Long id, @Param("status") UserStatus status);
// 批量删除
@Modifying
@Query("DELETE FROM User u WHERE u.status = :status")
int deleteUsersByStatus(@Param("status") UserStatus status);
}
复杂查询和动态查询
@Service
public class UserService {
private final UserRepository userRepository;
private final EntityManager entityManager;
public UserService(UserRepository userRepository, EntityManager entityManager) {
this.userRepository = userRepository;
this.entityManager = entityManager;
}
// Criteria API动态查询
public Page<User> searchUsers(UserSearchCriteria criteria, Pageable pageable) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
List<Predicate> predicates = new ArrayList<>();
// 动态构建查询条件
if (StringUtils.hasText(criteria.getUsername())) {
predicates.add(cb.like(root.get("username"), "%" + criteria.getUsername() + "%"));
}
if (StringUtils.hasText(criteria.getEmail())) {
predicates.add(cb.equal(root.get("email"), criteria.getEmail()));
}
if (criteria.getStatus() != null) {
predicates.add(cb.equal(root.get("status"), criteria.getStatus()));
}
if (criteria.getStartDate() != null) {
predicates.add(cb.greaterThanOrEqualTo(root.get("createdAt"), criteria.getStartDate()));
}
if (criteria.getEndDate() != null) {
predicates.add(cb.lessThanOrEqualTo(root.get("createdAt"), criteria.getEndDate()));
}
query.where(predicates.toArray(new Predicate[0]));
query.orderBy(cb.desc(root.get("createdAt")));
// 执行分页查询
TypedQuery<User> typedQuery = entityManager.createQuery(query);
int totalRows = typedQuery.getResultList().size();
typedQuery.setFirstResult((int) pageable.getOffset());
typedQuery.setMaxResults(pageable.getPageSize());
List<User> users = typedQuery.getResultList();
return new PageImpl<>(users, pageable, totalRows);
}
// 原生SQL复杂查询
public List<UserStatisticDTO> getUserStatistics() {
String sql = """
SELECT
u.status,
COUNT(*) as user_count,
DATE_FORMAT(u.created_at, '%Y-%m') as month
FROM users u
WHERE u.created_at >= DATE_SUB(NOW(), INTERVAL 12 MONTH)
GROUP BY u.status, DATE_FORMAT(u.created_at, '%Y-%m')
ORDER BY month DESC, u.status
""";
Query query = entityManager.createNativeQuery(sql);
List<Object[]> results = query.getResultList();
return results.stream()
.map(row -> new UserStatisticDTO(
(String) row[0],
((Number) row[1]).longValue(),
(String) row[2]
))
.collect(Collectors.toList());
}
}
7.3 事务管理
声明式事务
@Service
@Transactional(readOnly = true) // 类级别默认只读事务
public class OrderService {
private final OrderRepository orderRepository;
private final ProductRepository productRepository;
private final UserRepository userRepository;
@Transactional // 覆盖类级别,设置为读写事务
@Transactional(isolation = Isolation.READ_COMMITTED,
propagation = Propagation.REQUIRED,
rollbackFor = Exception.class,
timeout = 30)
public Order createOrder(OrderRequest request) {
// 1. 检查用户
User user = userRepository.findById(request.getUserId())
.orElseThrow(() -> new UserNotFoundException("用户不存在"));
// 2. 检查库存
Product product = productRepository.findById(request.getProductId())
.orElseThrow(() -> new ProductNotFoundException("商品不存在"));
if (product.getStock() < request.getQuantity()) {
throw new InsufficientStockException("库存不足");
}
// 3. 扣减库存
product.setStock(product.getStock() - request.getQuantity());
productRepository.save(product);
// 4. 创建订单
Order order = new Order();
order.setUser(user);
order.setProduct(product);
order.setQuantity(request.getQuantity());
order.setPrice(product.getPrice());
order.setStatus(OrderStatus.PENDING);
return orderRepository.save(order);
}
// 只读事务
@Transactional(readOnly = true)
public Order getOrderWithDetails(Long orderId) {
return orderRepository.findById(orderId)
.map(order -> {
// 延迟加载关联数据
order.getUser().getUsername(); // 触发用户信息加载
order.getProduct().getName(); // 触发商品信息加载
return order;
})
.orElseThrow(() -> new OrderNotFoundException("订单不存在"));
}
// 新事务传播
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logOrderCreation(Order order) {
// 在新事务中记录日志,不受主事务回滚影响
OrderLog log = new OrderLog();
log.setOrderId(order.getId());
log.setAction("CREATE");
log.setCreatedAt(new Date());
orderLogRepository.save(log);
}
}
编程式事务
@Service
public class TransactionService {
private final TransactionTemplate transactionTemplate;
private final UserRepository userRepository;
private final AuditRepository auditRepository;
public TransactionService(PlatformTransactionManager transactionManager,
UserRepository userRepository,
AuditRepository auditRepository) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
this.userRepository = userRepository;
this.auditRepository = auditRepository;
}
// 编程式事务示例
public User createUserWithAudit(UserCreationRequest request) {
return transactionTemplate.execute(status -> {
try {
// 创建用户
User user = new User();
user.setUsername(request.getUsername());
user.setEmail(request.getEmail());
user.setPassword(passwordEncoder.encode(request.getPassword()));
User savedUser = userRepository.save(user);
// 记录审计日志
AuditLog auditLog = new AuditLog();
auditLog.setAction("USER_CREATE");
auditLog.setUserId(savedUser.getId());
auditLog.setDetails("Created user: " + savedUser.getUsername());
auditRepository.save(auditLog);
return savedUser;
} catch (Exception e) {
status.setRollbackOnly(); // 手动标记回滚
throw new UserCreationException("创建用户失败", e);
}
});
}
// 复杂事务处理
public void batchUpdateUsers(List<UserUpdateRequest> requests) {
transactionTemplate.executeWithoutResult(status -> {
for (UserUpdateRequest request : requests) {
try {
updateUserInternal(request);
} catch (Exception e) {
// 记录失败但继续处理其他用户
System.err.println("更新用户失败: " + request.getUserId() + ", 错误: " + e.getMessage());
}
}
});
}
private void updateUserInternal(UserUpdateRequest request) {
User user = userRepository.findById(request.getUserId())
.orElseThrow(() -> new UserNotFoundException("用户不存在"));
// 更新用户信息
if (request.getUsername() != null) {
user.setUsername(request.getUsername());
}
if (request.getEmail() != null) {
user.setEmail(request.getEmail());
}
userRepository.save(user);
}
}
7.4 数据访问性能优化
连接池优化
@Configuration
public class DataSourceOptimizationConfig {
@Bean
@Primary
@ConfigurationProperties("spring.datasource.hikari")
public HikariConfig hikariConfig() {
HikariConfig config = new HikariConfig();
// 基础配置
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("root");
config.setPassword("password");
config.setDriverClassName("com.mysql.cj.jdbc.Driver");
// 性能优化配置
config.setMaximumPoolSize(20); // 最大连接数
config.setMinimumIdle(5); // 最小空闲连接
config.setIdleTimeout(300000); // 空闲超时 5分钟
config.setConnectionTimeout(20000); // 连接超时 20秒
config.setMaxLifetime(1800000); // 连接最大存活 30分钟
config.setLeakDetectionThreshold(60000); // 连接泄漏检测 1分钟
// 连接池名称
config.setPoolName("OptimizedHikariPool");
// 连接验证
config.setConnectionTestQuery("SELECT 1");
config.setValidationTimeout(3000);
// 性能相关
config.setAutoCommit(false); // 禁用自动提交
config.setAllowPoolSuspension(false); // 禁止池暂停
return config;
}
@Bean
@Primary
public DataSource dataSource(HikariConfig hikariConfig) {
return new HikariDataSource(hikariConfig);
}
}
批量操作优化
@Repository
public class BatchUserRepository {
private final JdbcTemplate jdbcTemplate;
private final EntityManager entityManager;
// 使用JdbcTemplate批量插入
public void batchInsertUsers(List<User> users) {
String sql = "INSERT INTO users (username, email, password, created_at) VALUES (?, ?, ?, ?)";
jdbcTemplate.batchUpdate(sql, users, users.size(),
(PreparedStatement ps, User user) -> {
ps.setString(1, user.getUsername());
ps.setString(2, user.getEmail());
ps.setString(3, user.getPassword());
ps.setTimestamp(4, new Timestamp(System.currentTimeMillis()));
});
}
// 使用Hibernate批量更新
@Transactional
public void batchUpdateUsers(List<User> users) {
for (int i = 0; i < users.size(); i++) {
User user = users.get(i);
entityManager.merge(user);
// 每50个实体清空一次持久化上下文
if (i % 50 == 0) {
entityManager.flush();
entityManager.clear();
}
}
}
// 原生SQL批量删除
public int batchDeleteInactiveUsers() {
String sql = """
DELETE FROM users
WHERE status = 'INACTIVE'
AND created_at < DATE_SUB(NOW(), INTERVAL 90 DAY)
""";
return jdbcTemplate.update(sql);
}
}
查询优化
@Service
public class OptimizedUserService {
private final UserRepository userRepository;
private final EntityManager entityManager;
// 使用EntityGraph解决N+1问题
@Transactional(readOnly = true)
public List<User> getUsersWithOrders() {
EntityGraph<?> graph = entityManager.createEntityGraph("User.orders");
return userRepository.findAll(graph);
}
// 使用@QueryHints优化查询
@Transactional(readOnly = true)
public List<User> getActiveUsers() {
return userRepository.findAll(
(root, query, cb) -> {
// 设置查询提示
query.setHint("javax.persistence.fetchgraph",
entityManager.getEntityGraph("User.orders"));
query.setHint("org.hibernate.cacheable", true);
query.setHint("org.hibernate.cacheMode", CacheMode.NORMAL);
return cb.equal(root.get("status"), UserStatus.ACTIVE);
}
);
}
// 分页查询优化
@Transactional(readOnly = true)
public Page<User> getUsersPage(Pageable pageable) {
// 使用countQuery优化分页计数
Specification<User> spec = (root, query, cb) -> {
// 主查询只查询ID
query.distinct(true);
return cb.equal(root.get("status"), UserStatus.ACTIVE);
};
return userRepository.findAll(spec, pageable);
}
// 使用投影减少数据传输
@Transactional(readOnly = true)
public List<UserProjection> getUserProjections() {
return userRepository.findAllProjectedBy();
}
}
// 接口投影
public interface UserProjection {
Long getId();
String getUsername();
String getEmail();
UserStatus getStatus();
}
// DTO投影
public class UserDTO {
private final Long id;
private final String username;
private final String email;
// 构造函数投影
public UserDTO(Long id, String username, String email) {
this.id = id;
this.username = username;
this.email = email;
}
// getters
}
8. Spring Boot监控和管理
8.1 Actuator端点配置
Spring Boot Actuator提供了生产级的监控和管理功能。
基础配置
# application.yml
management:
# 端点配置
endpoints:
web:
exposure:
include: "*"
exclude: "env,beans"
base-path: /actuator
# 端点详情配置
endpoint:
health:
show-details: always
show-components: always
info:
enabled: true
metrics:
enabled: true
shutdown:
enabled: true # 启用关闭端点(生产环境谨慎使用)
# 服务器配置
server:
port: 8081 # 管理端口与应用端口分离
# 健康检查配置
health:
defaults:
enabled: true
redis:
enabled: true
db:
enabled: true
# 指标配置
metrics:
export:
jmx:
enabled: true
prometheus:
enabled: true
tags:
application: ${spring.application.name}
environment: ${spring.profiles.active}
自定义健康检查
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
private final DataSource dataSource;
@Override
public Health health() {
try (Connection connection = dataSource.getConnection()) {
if (connection.isValid(1)) {
return Health.up()
.withDetail("database", "MySQL")
.withDetail("validationQuery", "SELECT 1")
.withDetail("url", dataSource.getConnection().getMetaData().getURL())
.build();
}
} catch (SQLException e) {
return Health.down()
.withDetail("error", e.getMessage())
.withException(e)
.build();
}
return Health.down()
.withDetail("error", "Unknown error")
.build();
}
}
// 自定义复合健康检查
@Component
public class ExternalServiceHealthIndicator implements HealthIndicator {
private final RestTemplate restTemplate;
@Override
public Health health() {
try {
ResponseEntity<String> response = restTemplate.getForEntity(
"http://external-service/health", String.class);
if (response.getStatusCode().is2xxSuccessful()) {
return Health.up()
.withDetail("service", "external-service")
.withDetail("status", response.getStatusCode())
.withDetail("response", response.getBody())
.build();
} else {
return Health.down()
.withDetail("service", "external-service")
.withDetail("status", response.getStatusCode())
.build();
}
} catch (Exception e) {
return Health.down(e)
.withDetail("service", "external-service")
.withDetail("error", e.getMessage())
.build();
}
}
}
自定义信息端点
@Component
public class CustomInfoContributor implements InfoContributor {
@Override
public void contribute(Info.Builder builder) {
builder.withDetail("application",
Map.of(
"name", "My Spring Boot Application",
"version", "1.0.0",
"description", "Demo application for monitoring",
"author", "Embrace"
))
.withDetail("build",
Map.of(
"timestamp", Instant.now().toString(),
"java", System.getProperty("java.version"),
"spring-boot", SpringBootVersion.getVersion()
))
.withDetail("git", getGitInfo());
}
private Map<String, Object> getGitInfo() {
try {
GitProperties gitProperties = getGitProperties();
return Map.of(
"branch", gitProperties.getBranch(),
"commit", gitProperties.getCommitId(),
"time", gitProperties.getCommitTime()
);
} catch (Exception e) {
return Map.of("error", "Git information not available");
}
}
}
8.2 自定义端点
创建自定义端点
@Component
@Endpoint(id = "custom")
public class CustomEndpoint {
private final UserService userService;
public CustomEndpoint(UserService userService) {
this.userService = userService;
}
// 读操作
@ReadOperation
public Map<String, Object> getUserStatistics() {
return Map.of(
"totalUsers", userService.getTotalUserCount(),
"activeUsers", userService.getActiveUserCount(),
"newUsersToday", userService.getNewUserCountToday(),
"timestamp", Instant.now()
);
}
// 写操作
@WriteOperation
public Map<String, Object> resetUserCache(@Selector String userId) {
userService.evictUserCache(Long.parseLong(userId));
return Map.of(
"message", "User cache cleared successfully",
"userId", userId,
"timestamp", Instant.now()
);
}
// 删除操作
@DeleteOperation
public Map<String, Object> deleteUser(@Selector String userId) {
userService.deleteUser(Long.parseLong(userId));
return Map.of(
"message", "User deleted successfully",
"userId", userId,
"timestamp", Instant.now()
);
}
}
// 带参数的自定义端点
@Component
@Endpoint(id = "user-metrics")
public class UserMetricsEndpoint {
@ReadOperation
public Map<String, Object> getUserMetrics(
@Selector(required = false) String userId,
@Nullable String metricType) {
if (userId != null) {
// 获取特定用户的指标
return getUserSpecificMetrics(userId, metricType);
} else {
// 获取整体用户指标
return getOverallUserMetrics(metricType);
}
}
private Map<String, Object> getUserSpecificMetrics(String userId, String metricType) {
return Map.of(
"userId", userId,
"metricType", metricType != null ? metricType : "all",
"loginCount", 42,
"lastLogin", Instant.now().minusSeconds(3600),
"sessionDuration", 1800
);
}
private Map<String, Object> getOverallUserMetrics(String metricType) {
return Map.of(
"totalUsers", 1250,
"activeUsers", 890,
"newUsersToday", 15,
"averageSessionDuration", 1650,
"peakConcurrentUsers", 342
);
}
}
8.3 指标收集
自定义指标
@Component
public class CustomMetrics {
private final MeterRegistry meterRegistry;
private final Counter userRegistrationCounter;
private final Timer userLoginTimer;
private final Gauge activeUserGauge;
public CustomMetrics(MeterRegistry meterRegistry, UserService userService) {
this.meterRegistry = meterRegistry;
// 计数器:记录用户注册
this.userRegistrationCounter = Counter.builder("user.registrations")
.description("Total number of user registrations")
.tag("application", "my-app")
.register(meterRegistry);
// 计时器:记录用户登录耗时
this.userLoginTimer = Timer.builder("user.login.time")
.description("Time taken for user login")
.tag("application", "my-app")
.register(meterRegistry);
// 仪表:记录活跃用户数
this.activeUserGauge = Gauge.builder("users.active")
.description("Number of active users")
.tag("application", "my-app")
.register(meterRegistry, userService, UserService::getActiveUserCount);
}
public void recordUserRegistration() {
userRegistrationCounter.increment();
}
public void recordUserLogin(Duration duration) {
userLoginTimer.record(duration);
}
// 方法级别的监控
@EventListener
public void handleUserRegistration(UserRegisteredEvent event) {
userRegistrationCounter.increment(
Tags.of("source", event.getSource(), "type", event.getUserType())
);
}
}
// 性能监控切面
@Aspect
@Component
public class PerformanceMonitoringAspect {
private final MeterRegistry meterRegistry;
public PerformanceMonitoringAspect(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@Around("@annotation(Monitored)")
public Object monitorMethod(ProceedingJoinPoint joinPoint, Monitored monitored) throws Throwable {
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getSimpleName();
Timer.Sample sample = Timer.start(meterRegistry);
try {
Object result = joinPoint.proceed();
// 记录成功指标
sample.stop(Timer.builder("method.execution.time")
.description("Method execution time")
.tag("class", className)
.tag("method", methodName)
.tag("status", "success")
.register(meterRegistry));
return result;
} catch (Exception e) {
// 记录失败指标
sample.stop(Timer.builder("method.execution.time")
.tag("class", className)
.tag("method", methodName)
.tag("status", "error")
.tag("exception", e.getClass().getSimpleName())
.register(meterRegistry));
throw e;
}
}
}
// 监控注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Monitored {
String value() default "";
}
8.4 日志管理
日志配置
# application.yml
logging:
level:
root: INFO
com.example.myapp: DEBUG
org.springframework.web: DEBUG
org.hibernate.SQL: DEBUG
org.hibernate.type.descriptor.sql.BasicBinder: TRACE
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level [%logger{36}] - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level [%logger{36}] - %msg%n"
file:
name: logs/application.log
max-size: 100MB
max-history: 30
logback:
rollingpolicy:
max-file-size: 100MB
total-size-cap: 1GB
自定义日志配置
<!-- logback-spring.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 引入Spring Boot默认配置 -->
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 文件输出 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/application.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/application.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 错误日志单独记录 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/error.log</file>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/error.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 异步日志 -->
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>1024</queueSize>
<appender-ref ref="FILE"/>
</appender>
<!-- Spring Profile配置 -->
<springProfile name="dev">
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>
<springProfile name="prod">
<root level="INFO">
<appender-ref ref="ASYNC_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</springProfile>
</configuration>
结构化日志
@Component
public class StructuredLogger {
private static final Logger logger = LoggerFactory.getLogger(StructuredLogger.class);
private final ObjectMapper objectMapper;
public StructuredLogger(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
public void logUserAction(String userId, String action, Map<String, Object> details) {
try {
Map<String, Object> logData = Map.of(
"timestamp", Instant.now(),
"userId", userId,
"action", action,
"details", details,
"sessionId", getCurrentSessionId(),
"requestId", getCurrentRequestId()
);
logger.info("USER_ACTION: {}", objectMapper.writeValueAsString(logData));
} catch (Exception e) {
logger.error("Failed to log user action", e);
}
}
public void logApiRequest(HttpServletRequest request, HttpServletResponse response, long duration) {
try {
Map<String, Object> logData = Map.of(
"timestamp", Instant.now(),
"method", request.getMethod(),
"uri", request.getRequestURI(),
"query", request.getQueryString(),
"userAgent", request.getHeader("User-Agent"),
"remoteAddr", request.getRemoteAddr(),
"statusCode", response.getStatus(),
"duration", duration,
"requestId", getCurrentRequestId()
);
logger.info("API_REQUEST: {}", objectMapper.writeValueAsString(logData));
} catch (Exception e) {
logger.error("Failed to log API request", e);
}
}
private String getCurrentSessionId() {
// 获取当前会话ID的逻辑
return UUID.randomUUID().toString(); // 简化示例
}
private String getCurrentRequestId() {
// 获取当前请求ID的逻辑
return MDC.get("requestId");
}
}
// 请求过滤器
@Component
public class RequestLoggingFilter implements Filter {
private final StructuredLogger structuredLogger;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
long startTime = System.currentTimeMillis();
String requestId = UUID.randomUUID().toString();
MDC.put("requestId", requestId);
try {
chain.doFilter(request, response);
} finally {
long duration = System.currentTimeMillis() - startTime;
structuredLogger.logApiRequest(httpRequest, httpResponse, duration);
MDC.remove("requestId");
}
}
}
8.5 安全配置
Actuator安全配置
@Configuration
public class ActuatorSecurityConfig {
@Bean
public SecurityFilterChain actuatorSecurityFilterChain(HttpSecurity http) throws Exception {
return http
.securityMatcher("/actuator/**")
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health", "/actuator/info").permitAll()
.requestMatchers("/actuator/metrics/**", "/actuator/prometheus").hasRole("MONITOR")
.requestMatchers("/actuator/shutdown").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(withDefaults())
.csrf(csrf -> csrf.disable()) // API端点通常不需要CSRF保护
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.build();
}
}
// 自定义用户详情服务
@Service
public class ActuatorUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if ("monitor".equals(username)) {
return User.builder()
.username("monitor")
.password(passwordEncoder().encode("monitor123"))
.roles("MONITOR")
.build();
}
if ("admin".equals(username)) {
return User.builder()
.username("admin")
.password(passwordEncoder().encode("admin123"))
.roles("ADMIN", "MONITOR")
.build();
}
throw new UsernameNotFoundException("User not found: " + username);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
9. Spring Boot测试体系
9.1 测试策略和分层
Spring Boot提供了完整的测试框架支持,包括单元测试、集成测试和Web层测试。
测试分层策略
单元测试 (Unit Tests)
├── Service层业务逻辑测试
├── Repository层数据访问测试
└── Utility工具类测试
集成测试 (Integration Tests)
├── 数据库集成测试
├── 外部服务集成测试
└── 消息队列集成测试
端到端测试 (E2E Tests)
├── Web API测试
├── 完整业务流程测试
└── 性能测试
测试配置文件
# application-test.yml
spring:
# 使用H2内存数据库进行测试
datasource:
url: jdbc:h2:mem:testdb
username: sa
password:
driver-class-name: org.h2.Driver
# JPA配置
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
properties:
hibernate:
format_sql: true
# Redis配置(使用嵌入式Redis)
data:
redis:
host: localhost
port: 6370
# 测试专用配置
main:
allow-bean-definition-overriding: true
logging:
level:
com.example.myapp: DEBUG
org.springframework.web: DEBUG
9.2 单元测试
Service层单元测试
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private PasswordEncoder passwordEncoder;
@Mock
private EmailService emailService;
@InjectMocks
private UserService userService;
@Test
@DisplayName("创建用户成功")
void createUser_Success() {
// Given
UserCreationRequest request = new UserCreationRequest();
request.setUsername("testuser");
request.setEmail("test@example.com");
request.setPassword("password123");
when(passwordEncoder.encode("password123")).thenReturn("encodedPassword");
when(userRepository.save(any(User.class))).thenAnswer(invocation -> {
User user = invocation.getArgument(0);
user.setId(1L);
return user;
});
// When
User result = userService.createUser(request);
// Then
assertThat(result).isNotNull();
assertThat(result.getUsername()).isEqualTo("testuser");
assertThat(result.getEmail()).isEqualTo("test@example.com");
assertThat(result.getPassword()).isEqualTo("encodedPassword");
verify(passwordEncoder).encode("password123");
verify(userRepository).save(any(User.class));
verify(emailService).sendWelcomeEmail(any(User.class));
}
@Test
@DisplayName("创建用户失败 - 用户名已存在")
void createUser_Fail_UsernameExists() {
// Given
UserCreationRequest request = new UserCreationRequest();
request.setUsername("existinguser");
when(userRepository.existsByUsername("existinguser")).thenReturn(true);
// When & Then
assertThatThrownBy(() -> userService.createUser(request))
.isInstanceOf(UsernameAlreadyExistsException.class)
.hasMessage("Username already exists: existinguser");
verify(userRepository).existsByUsername("existinguser");
verifyNoMoreInteractions(userRepository);
verifyNoInteractions(passwordEncoder, emailService);
}
@Test
@DisplayName("获取用户成功")
void getUserById_Success() {
// Given
User user = new User();
user.setId(1L);
user.setUsername("testuser");
when(userRepository.findById(1L)).thenReturn(Optional.of(user));
// When
Optional<User> result = userService.getUserById(1L);
// Then
assertThat(result).isPresent();
assertThat(result.get().getUsername()).isEqualTo("testuser");
verify(userRepository).findById(1L);
}
}
Repository层单元测试
@ExtendWith(MockitoExtension.class)
class UserRepositoryTest {
@Mock
private EntityManager entityManager;
@InjectMocks
private UserRepositoryImpl userRepository;
@Test
@DisplayName("根据用户名查找用户")
void findByUsername_Success() {
// Given
String username = "testuser";
User expectedUser = new User();
expectedUser.setUsername(username);
when(entityManager.createQuery(anyString(), User.class)).thenReturn(mock(Query.class));
when(entityManager.createQuery(anyString(), User.class).setParameter(anyString(), any()))
.thenReturn(mock(Query.class));
when(entityManager.createQuery(anyString(), User.class)
.setParameter(anyString(), any()).getSingleResult()).thenReturn(expectedUser);
// When
User result = userRepository.findByUsername(username);
// Then
assertThat(result).isNotNull();
assertThat(result.getUsername()).isEqualTo(username);
}
@Test
@DisplayName("统计活跃用户数量")
void countActiveUsers_Success() {
// Given
Long expectedCount = 150L;
when(entityManager.createQuery(anyString(), Long.class)).thenReturn(mock(Query.class));
when(entityManager.createQuery(anyString(), Long.class).getSingleResult())
.thenReturn(expectedCount);
// When
Long result = userRepository.countActiveUsers();
// Then
assertThat(result).isEqualTo(expectedCount);
}
}
9.3 集成测试
数据库集成测试
@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.yml")
@Transactional
@Rollback
class UserRepositoryIntegrationTest {
@Autowired
private UserRepository userRepository;
@Autowired
private TestEntityManager entityManager;
@Test
@DisplayName("保存和查询用户")
void saveAndFindUser_Success() {
// Given
User user = new User();
user.setUsername("testuser");
user.setEmail("test@example.com");
user.setPassword("encodedPassword");
user.setStatus(UserStatus.ACTIVE);
// When
User savedUser = userRepository.save(user);
User foundUser = userRepository.findById(savedUser.getId()).orElse(null);
// Then
assertThat(savedUser).isNotNull();
assertThat(savedUser.getId()).isNotNull();
assertThat(foundUser).isNotNull();
assertThat(foundUser.getUsername()).isEqualTo("testuser");
assertThat(foundUser.getEmail()).isEqualTo("test@example.com");
}
@Test
@DisplayName("根据用户名查找用户")
void findByUsername_Success() {
// Given
User user = new User();
user.setUsername("testuser");
user.setEmail("test@example.com");
entityManager.persistAndFlush(user);
// When
Optional<User> foundUser = userRepository.findByUsername("testuser");
// Then
assertThat(foundUser).isPresent();
assertThat(foundUser.get().getUsername()).isEqualTo("testuser");
}
@Test
@DisplayName("分页查询用户")
void findAllWithPagination_Success() {
// Given - 创建测试数据
for (int i = 1; i <= 25; i++) {
User user = new User();
user.setUsername("user" + i);
user.setEmail("user" + i + "@example.com");
user.setStatus(UserStatus.ACTIVE);
entityManager.persistAndFlush(user);
}
Pageable pageable = PageRequest.of(0, 10, Sort.by("username"));
// When
Page<User> userPage = userRepository.findAll(pageable);
// Then
assertThat(userPage.getContent()).hasSize(10);
assertThat(userPage.getTotalElements()).isEqualTo(25);
assertThat(userPage.getTotalPages()).isEqualTo(3);
assertThat(userPage.isFirst()).isTrue();
assertThat(userPage.hasNext()).isTrue();
}
@Test
@DisplayName("使用原生SQL查询")
void countUsersByStatus_NativeQuery_Success() {
// Given
createUsersWithStatus(UserStatus.ACTIVE, 10);
createUsersWithStatus(UserStatus.INACTIVE, 5);
// When
Long activeCount = userRepository.countUsersByStatus(UserStatus.ACTIVE);
Long inactiveCount = userRepository.countUsersByStatus(UserStatus.INACTIVE);
// Then
assertThat(activeCount).isEqualTo(10);
assertThat(inactiveCount).isEqualTo(5);
}
private void createUsersWithStatus(UserStatus status, int count) {
for (int i = 1; i <= count; i++) {
User user = new User();
user.setUsername("user_" + status + "_" + i);
user.setEmail("user" + i + "@example.com");
user.setStatus(status);
entityManager.persistAndFlush(user);
}
}
}
Web层集成测试
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(locations = "classpath:application-test.yml")
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@AutoConfigureMockMvc
class UserControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private UserService userService;
@Test
@DisplayName("创建用户API测试")
void createUser_Success() throws Exception {
// Given
UserCreationRequest request = new UserCreationRequest();
request.setUsername("newuser");
request.setEmail("newuser@example.com");
request.setPassword("password123");
User createdUser = new User();
createdUser.setId(1L);
createdUser.setUsername("newuser");
createdUser.setEmail("newuser@example.com");
when(userService.createUser(any(UserCreationRequest.class))).thenReturn(createdUser);
// When & Then
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.username").value("newuser"))
.andExpect(jsonPath("$.email").value("newuser@example.com"))
.andExpect(header().string("Location", "/api/users/1"));
verify(userService).createUser(any(UserCreationRequest.class));
}
@Test
@DisplayName("获取用户列表API测试")
void getUsers_Success() throws Exception {
// Given
List<User> users = Arrays.asList(
createTestUser(1L, "user1", "user1@example.com"),
createTestUser(2L, "user2", "user2@example.com")
);
when(userService.getUsers(any(Pageable.class))).thenReturn(new PageImpl<>(users));
// When & Then
mockMvc.perform(get("/api/users")
.param("page", "0")
.param("size", "10")
.param("sort", "username,asc"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(2)))
.andExpect(jsonPath("$.content[0].id").value(1))
.andExpect(jsonPath("$.content[0].username").value("user1"))
.andExpect(jsonPath("$.totalElements").value(2))
.andExpect(jsonPath("$.totalPages").value(1))
.andExpect(jsonPath("$.size").value(10));
verify(userService).getUsers(any(Pageable.class));
}
@Test
@DisplayName("获取用户详情API测试")
void getUserById_Success() throws Exception {
// Given
User user = createTestUser(1L, "testuser", "test@example.com");
when(userService.getUserById(1L)).thenReturn(Optional.of(user));
// When & Then
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.username").value("testuser"))
.andExpect(jsonPath("$.email").value("test@example.com"));
verify(userService).getUserById(1L);
}
@Test
@DisplayName("用户不存在返回404")
void getUserById_NotFound() throws Exception {
// Given
when(userService.getUserById(999L)).thenReturn(Optional.empty());
// When & Then
mockMvc.perform(get("/api/users/999"))
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.error").value("User not found"));
verify(userService).getUserById(999L);
}
@Test
@DisplayName("验证请求参数")
void createUser_ValidationFailed() throws Exception {
// Given
UserCreationRequest request = new UserCreationRequest();
request.setUsername(""); // 无效的用户名
request.setEmail("invalid-email"); // 无效的邮箱
request.setPassword("123"); // 密码太短
// When & Then
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.fieldErrors", hasSize(3)))
.andExpect(jsonPath("$.fieldErrors[?(@.field == 'username')].message").value("Username is required"))
.andExpect(jsonPath("$.fieldErrors[?(@.field == 'email')].message").value("Invalid email format"))
.andExpect(jsonPath("$.fieldErrors[?(@.field == 'password')].message").value("Password must be at least 6 characters"));
verifyNoInteractions(userService);
}
private User createTestUser(Long id, String username, String email) {
User user = new User();
user.setId(id);
user.setUsername(username);
user.setEmail(email);
user.setStatus(UserStatus.ACTIVE);
return user;
}
}
9.4 测试工具和实用类
测试数据构建器
public class UserTestDataBuilder {
private User user;
public UserTestDataBuilder() {
this.user = new User();
}
public static UserTestDataBuilder aUser() {
return new UserTestDataBuilder();
}
public UserTestDataBuilder withId(Long id) {
user.setId(id);
return this;
}
public UserTestDataBuilder withUsername(String username) {
user.setUsername(username);
return this;
}
public UserTestDataBuilder withEmail(String email) {
user.setEmail(email);
return this;
}
public UserTestDataBuilder withStatus(UserStatus status) {
user.setStatus(status);
return this;
}
public UserTestDataBuilder withPassword(String password) {
user.setPassword(password);
return this;
}
public UserTestDataBuilder withCreatedAt(Date createdAt) {
user.setCreatedAt(createdAt);
return this;
}
public User build() {
return user;
}
// 预定义的测试用户
public static User aDefaultUser() {
return aUser()
.withUsername("testuser")
.withEmail("test@example.com")
.withStatus(UserStatus.ACTIVE)
.withPassword("encodedPassword")
.withCreatedAt(new Date())
.build();
}
public static List<User> multipleUsers(int count) {
List<User> users = new ArrayList<>();
for (int i = 1; i <= count; i++) {
users.add(aUser()
.withId((long) i)
.withUsername("user" + i)
.withEmail("user" + i + "@example.com")
.withStatus(UserStatus.ACTIVE)
.build());
}
return users;
}
}
// 请求DTO构建器
public class UserCreationRequestBuilder {
private UserCreationRequest request;
public UserCreationRequestBuilder() {
this.request = new UserCreationRequest();
}
public static UserCreationRequestBuilder aUserCreationRequest() {
return new UserCreationRequestBuilder();
}
public UserCreationRequestBuilder withUsername(String username) {
request.setUsername(username);
return this;
}
public UserCreationRequestBuilder withEmail(String email) {
request.setEmail(email);
return this;
}
public UserCreationRequestBuilder withPassword(String password) {
request.setPassword(password);
return this;
}
public UserCreationRequest build() {
return request;
}
public static UserCreationRequest aDefaultUserCreationRequest() {
return aUserCreationRequest()
.withUsername("newuser")
.withEmail("newuser@example.com")
.withPassword("password123")
.build();
}
}
测试工具类
public class TestUtils {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
static {
OBJECT_MAPPER.registerModule(new JavaTimeModule());
OBJECT_MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
/**
* 将对象转换为JSON字符串
*/
public static String asJsonString(Object obj) throws JsonProcessingException {
return OBJECT_MAPPER.writeValueAsString(obj);
}
/**
* 从JSON字符串转换为对象
*/
public static <T> T fromJsonString(String json, Class<T> clazz) throws JsonProcessingException {
return OBJECT_MAPPER.readValue(json, clazz);
}
/**
* 创建测试用的MockHttpServletResponse
*/
public static MockHttpServletResponse createMockResponse() {
return new MockHttpServletResponse();
}
/**
* 验证JSON响应内容
*/
public static void assertJsonResponse(MockMvcResultActions resultActions,
String expectedJsonPath,
Object expectedValue) throws Exception {
resultActions.andExpect(jsonPath(expectedJsonPath).value(expectedValue));
}
/**
* 验证分页响应结构
*/
public static void assertPageResponse(MockMvcResultActions resultActions,
int expectedSize,
long expectedTotalElements) throws Exception {
resultActions
.andExpect(jsonPath("$.content").isArray())
.andExpect(jsonPath("$.content", hasSize(expectedSize)))
.andExpect(jsonPath("$.totalElements").value(expectedTotalElements))
.andExpect(jsonPath("$.size").exists())
.andExpect(jsonPath("$.number").exists())
.andExpect(jsonPath("$.totalPages").exists())
.andExpect(jsonPath("$.first").exists())
.andExpect(jsonPath("$.last").exists());
}
/**
* 等待异步操作完成
*/
public static void waitForAsyncTask(long timeoutMillis) throws InterruptedException {
Thread.sleep(timeoutMillis);
}
/**
* 生成随机字符串
*/
public static String randomString(int length) {
return UUID.randomUUID().toString().replace("-", "").substring(0, Math.min(length, 32));
}
/**
* 生成随机邮箱
*/
public static String randomEmail() {
return "test" + System.currentTimeMillis() + "@example.com";
}
/**
* 创建测试用的ultipartFile
*/
public static MockMultipartFile createTestFile(String filename, String content) {
return new MockMultipartFile(
"file",
filename,
MediaType.TEXT_PLAIN_VALUE,
content.getBytes()
);
}
}
9.5 测试配置和最佳实践
测试配置类
@TestConfiguration
public class TestConfig {
@Bean
@Primary
public DataSource testDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.setName("testdb")
.addScript("schema.sql")
.addScript("test-data.sql")
.build();
}
@Bean
@Primary
public PasswordEncoder testPasswordEncoder() {
return NoOpPasswordEncoder.getInstance(); // 测试环境使用明文密码
}
@Bean
public TestDataInitializer testDataInitializer(UserRepository userRepository) {
return new TestDataInitializer(userRepository);
}
}
// 测试数据初始化器
@Component
public class TestDataInitializer implements ApplicationListener<ContextRefreshedEvent> {
private final UserRepository userRepository;
private boolean initialized = false;
public TestDataInitializer(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (!initialized) {
initializeTestData();
initialized = true;
}
}
private void initializeTestData() {
// 创建测试用户
User admin = UserTestDataBuilder.aUser()
.withUsername("admin")
.withEmail("admin@example.com")
.withStatus(UserStatus.ACTIVE)
.withPassword("admin123")
.build();
userRepository.save(admin);
User testUser = UserTestDataBuilder.aUser()
.withUsername("testuser")
.withEmail("test@example.com")
.withStatus(UserStatus.ACTIVE)
.withPassword("test123")
.build();
userRepository.save(testUser);
}
}
测试基类
@ExtendWith(MockitoExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(locations = "classpath:application-test.yml")
@Transactional
@Rollback
public abstract class BaseIntegrationTest {
@Autowired
protected TestEntityManager entityManager;
@Autowired
protected UserRepository userRepository;
@Autowired
protected ObjectMapper objectMapper;
protected User createTestUser(String username, String email) {
User user = UserTestDataBuilder.aUser()
.withUsername(username)
.withEmail(email)
.withStatus(UserStatus.ACTIVE)
.build();
return entityManager.persistAndFlush(user);
}
protected void flushAndClear() {
entityManager.flush();
entityManager.clear();
}
protected <T> T persistAndFlush(T entity) {
return entityManager.persistAndFlush(entity);
}
protected void executeInTransaction(Runnable operation) {
operation.run();
}
}
// API测试基类
public abstract class BaseApiTest {
@Autowired
protected MockMvc mockMvc;
@Autowired
protected ObjectMapper objectMapper;
@MockBean
protected UserService userService;
protected ResultActions performGet(String url, Object... uriVars) throws Exception {
return mockMvc.perform(get(url, uriVars));
}
protected ResultActions performPost(String url, Object requestBody) throws Exception {
return mockMvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(requestBody)));
}
protected ResultActions performPut(String url, Object requestBody, Object... uriVars) throws Exception {
return mockMvc.perform(put(url, uriVars)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(requestBody)));
}
protected ResultActions performDelete(String url, Object... uriVars) throws Exception {
return mockMvc.perform(delete(url, uriVars));
}
protected void assertSuccess(ResultActions resultActions) throws Exception {
resultActions.andExpect(status().is2xxSuccessful());
}
protected void assertNotFound(ResultActions resultActions) throws Exception {
resultActions.andExpect(status().isNotFound());
}
protected void assertBadRequest(ResultActions resultActions) throws Exception {
resultActions.andExpect(status().isBadRequest());
}
}
10. Spring Boot性能优化
10.1 启动性能优化
延迟初始化配置
# application.yml
spring:
main:
lazy-initialization: true # 启用延迟初始化
# 排除不必要的自动配置
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
- org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
- org.springframework.boot.autoconfigure.websocket.WebSocketServletAutoConfiguration
# JPA优化
jpa:
open-in-view: false # 禁用Open Session in View模式
show-sql: false # 生产环境关闭SQL日志
properties:
hibernate:
# 启动时验证表结构,创建时跳过
hbm2ddl:
auto: validate
# 二级缓存配置
cache:
use_second_level_cache: true
region:
factory_class: org.hibernate.cache.ehcache.EhCacheRegionFactory
# 查询缓存
cache:
use_query_cache: true
自定义启动优化配置
@Configuration
public class StartupOptimizationConfig {
// 禁用不必要的Bean
@Bean
@ConditionalOnMissingBean
public static BeanFactoryPostProcessor beanFactoryPostProcessor() {
return beanFactory -> {
// 禁用一些启动时的验证
String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
if (beanName.contains("Health") || beanName.contains("Metrics")) {
// 可以根据需要延迟加载监控相关Bean
System.out.println("延迟加载Bean: " + beanName);
}
}
};
}
// 优化Bean创建顺序
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public ApplicationContextInitializer<ConfigurableApplicationContext> contextInitializer() {
return context -> {
// 设置属性源优化
ConfigurableEnvironment environment = context.getEnvironment();
environment.getPropertySources().addFirst(new MapPropertySource("optimization",
Map.of("spring.main.lazy-initialization", "true")));
};
}
}
启动时间监控和分析
@Component
public class StartupPerformanceAnalyzer implements ApplicationListener<ApplicationReadyEvent> {
private static final Map<String, Long> phaseTimestamps = new ConcurrentHashMap<>();
private long applicationStartTime;
@EventListener
public void onApplicationStarting(ApplicationStartingEvent event) {
applicationStartTime = System.currentTimeMillis();
phaseTimestamps.put("STARTING", applicationStartTime);
logPhase("应用开始启动");
}
@EventListener
public void onApplicationReady(ApplicationReadyEvent event) {
long readyTime = System.currentTimeMillis();
phaseTimestamps.put("READY", readyTime);
long totalTime = readyTime - applicationStartTime;
logPhase("应用启动完成,总耗时: " + totalTime + "ms");
analyzeStartupPerformance(totalTime);
}
private void logPhase(String phase) {
long currentTime = System.currentTimeMillis();
long phaseTime = currentTime - applicationStartTime;
System.out.println(String.format("[%dms] %s", phaseTime, phase));
}
private void analyzeStartupPerformance(long totalTime) {
System.out.println("\n=== 启动性能分析报告 ===");
// 性能分级
if (totalTime < 3000) {
System.out.println("✅ 启动性能: 优秀 (< 3s)");
} else if (totalTime < 6000) {
System.out.println("⚠️ 启动性能: 良好 (3-6s)");
} else {
System.out.println("❌ 启动性能: 需要优化 (> 6s)");
}
// 优化建议
System.out.println("\n🚀 优化建议:");
if (totalTime > 5000) {
System.out.println("1. 启用延迟初始化 (spring.main.lazy-initialization=true)");
System.out.println("2. 检查数据库连接配置");
System.out.println("3. 优化Bean初始化逻辑");
}
System.out.println("4. 排除不必要的自动配置");
System.out.println("5. 使用Spring Boot DevTools的快捷重启");
System.out.println("6. 考虑使用GraalVM Native Image");
}
}
10.2 运行时性能优化
JVM参数优化
# 生产环境JVM启动参数
java -Xms1g -Xmx2g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:G1HeapRegionSize=16m \
-XX:+UnlockExperimentalVMOptions \
-XX:+UseZGC \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/app/heapdump.hprof \
-XX:+PrintGCDetails \
-XX:+PrintGCTimeStamps \
-Xloggc:/var/log/app/gc.log \
-jar application.jar
连接池优化配置
# HikariCP优化配置
spring:
datasource:
hikari:
# 基础配置
maximum-pool-size: 20 # 根据CPU核心数和并发量调整
minimum-idle: 10 # 设置为maximum-pool-size的一半
idle-timeout: 300000 # 5分钟空闲超时
max-lifetime: 1800000 # 30分钟最大生命周期
connection-timeout: 20000 # 20秒连接超时
leak-detection-threshold: 60000 # 1分钟连接泄漏检测
# 性能优化配置
auto-commit: false # 手动控制事务
allow-pool-suspension: false # 不允许池暂停
initialization-fail-timeout: 1 # 启动失败快速失败
# 连接池名称(便于监控)
pool-name: "AppHikariCP"
# Redis连接池优化
data:
redis:
lettuce:
pool:
max-active: 50
max-idle: 20
min-idle: 5
max-wait: 5000ms
shutdown-timeout: 100ms
线程池优化
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
@Bean(name = "taskExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数:CPU核心数
int corePoolSize = Runtime.getRuntime().availableProcessors();
executor.setCorePoolSize(corePoolSize);
// 最大线程数:CPU核心数 * 2
executor.setMaxPoolSize(corePoolSize * 2);
// 队列容量
executor.setQueueCapacity(1000);
// 线程空闲时间
executor.setKeepAliveSeconds(60);
// 线程名前缀
executor.setThreadNamePrefix("async-task-");
// 拒绝策略:由调用线程执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待任务完成后关闭
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (throwable, method, objects) -> {
System.err.println("Async method exception: " + method.getName() + " - " + throwable.getMessage());
throwable.printStackTrace();
};
}
}
10.3 内存优化
内存监控和分析
@Component
public class MemoryMonitor {
private final MeterRegistry meterRegistry;
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public MemoryMonitor(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
startMemoryMonitoring();
}
private void startMemoryMonitoring() {
scheduler.scheduleAtFixedRate(() -> {
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapUsage = memoryMXBean.getHeapMemoryUsage();
MemoryUsage nonHeapUsage = memoryMXBean.getNonHeapMemoryUsage();
// 记录内存使用指标
meterRegistry.gauge("jvm.memory.heap.used", heapUsage.getUsed());
meterRegistry.gauge("jvm.memory.heap.max", heapUsage.getMax());
meterRegistry.gauge("jvm.memory.heap.usage",
(double) heapUsage.getUsed() / heapUsage.getMax());
meterRegistry.gauge("jvm.memory.nonheap.used", nonHeapUsage.getUsed());
meterRegistry.gauge("jvm.memory.nonheap.max", nonHeapUsage.getMax());
// 检查内存使用情况
double heapUsageRatio = (double) heapUsage.getUsed() / heapUsage.getMax();
if (heapUsageRatio > 0.8) {
System.err.println("⚠️ 堆内存使用率过高: " + String.format("%.2f%%", heapUsageRatio * 100));
// 可以触发GC或其他清理操作
System.gc();
}
}, 30, 30, TimeUnit.SECONDS);
}
@PreDestroy
public void shutdown() {
scheduler.shutdown();
}
}
缓存优化配置
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30)) // 默认过期时间30分钟
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.disableCachingNullValues(); // 不缓存null值
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.transactionAware() // 支持事务
.build();
}
@Bean
public CacheManager concurrentMapCacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
cacheManager.setCacheNames("users", "products", "categories");
cacheManager.setAllowNullValues(false); // 不缓存null值
return cacheManager;
}
@Bean
public CaffeineCacheManager caffeineCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.maximumSize(1000) // 最大缓存条目数
.expireAfterWrite(Duration.ofMinutes(10)) // 写入后10分钟过期
.expireAfterAccess(Duration.ofMinutes(5)) // 访问后5分钟过期
.recordStats() // 记录统计信息
.removalListener((key, value, cause) ->
System.out.println("Cache entry removed: " + key + ", cause: " + cause)));
return cacheManager;
}
}
缓存使用优化
@Service
public class OptimizedUserService {
private final UserRepository userRepository;
private final CacheManager cacheManager;
// 使用缓存
@Cacheable(value = "users", key = "#id", unless = "#result == null")
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
// 更新缓存
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
return userRepository.save(user);
}
// 删除缓存
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
// 批量缓存操作
@Cacheable(value = "users", key = "'list:' + #pageable.pageNumber + ':' + #pageable.pageSize")
public Page<User> getUsers(Pageable pageable) {
return userRepository.findAll(pageable);
}
// 条件缓存
@Cacheable(value = "active-users", condition = "#status == 'ACTIVE'")
public List<User> getUsersByStatus(UserStatus status) {
return userRepository.findByStatus(status);
}
// 缓存预热
@PostConstruct
public void preloadCache() {
// 预加载热点数据
List<User> activeUsers = userRepository.findByStatus(UserStatus.ACTIVE);
Cache cache = cacheManager.getCache("active-users");
if (cache != null) {
cache.put("ACTIVE", activeUsers);
}
}
// 手动缓存操作
public void evictUserCache(String username) {
Cache cache = cacheManager.getCache("users");
if (cache != null) {
cache.evictIfPresent(username);
}
}
// 缓存统计信息
public void printCacheStats() {
Cache usersCache = cacheManager.getCache("users");
if (usersCache instanceof com.github.benmanes.caffeine.cache.Cache) {
com.github.benmanes.caffeine.cache.Cache<Object, Object> caffeineCache =
(com.github.benmanes.caffeine.cache.Cache<Object, Object>) usersCache.getNativeCache();
System.out.println("Cache stats: " + caffeineCache.stats());
}
}
}
10.4 数据库性能优化
SQL查询优化
@Repository
public class OptimizedUserRepository {
private final EntityManager entityManager;
private final JdbcTemplate jdbcTemplate;
// 使用EntityGraph避免N+1查询问题
@EntityGraph(attributePaths = {"orders", "roles"})
public List<User> findUsersWithOrdersAndRoles() {
String jpql = "SELECT u FROM User u WHERE u.status = :status";
return entityManager.createQuery(jpql, User.class)
.setParameter("status", UserStatus.ACTIVE)
.getResultList();
}
// 批量查询优化
public List<User> findUsersByIds(List<Long> userIds) {
if (userIds.isEmpty()) {
return Collections.emptyList();
}
// 分批查询,避免IN子句过长
int batchSize = 1000;
List<User> result = new ArrayList<>();
for (int i = 0; i < userIds.size(); i += batchSize) {
int end = Math.min(i + batchSize, userIds.size());
List<Long> batch = userIds.subList(i, end);
String jpql = "SELECT u FROM User u WHERE u.id IN (:ids)";
List<User> batchResult = entityManager.createQuery(jpql, User.class)
.setParameter("ids", batch)
.getResultList();
result.addAll(batchResult);
}
return result;
}
// 分页查询优化
public Page<User> findUsersWithPagination(UserSearchCriteria criteria, Pageable pageable) {
// 使用countQuery优化分页计数
Specification<User> spec = (root, query, cb) -> {
// 主查询只查询ID,减少数据传输
query.multiselect(root.get("id"));
List<Predicate> predicates = new ArrayList<>();
if (criteria.getUsername() != null) {
predicates.add(cb.like(root.get("username"), "%" + criteria.getUsername() + "%"));
}
if (criteria.getStatus() != null) {
predicates.add(cb.equal(root.get("status"), criteria.getStatus()));
}
return cb.and(predicates.toArray(new Predicate[0]));
};
return userRepository.findAll(spec, pageable);
}
// 原生SQL优化
public List<UserStatisticsDTO> getUserStatistics() {
String sql = """
SELECT
u.status,
COUNT(*) as user_count,
DATE(u.created_at) as created_date
FROM users u
WHERE u.created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY u.status, DATE(u.created_at)
ORDER BY created_date DESC
""";
return jdbcTemplate.query(sql, (rs, rowNum) ->
new UserStatisticsDTO(
rs.getString("status"),
rs.getLong("user_count"),
rs.getDate("created_date")
));
}
// 批量操作优化
@Transactional
public void batchInsertUsers(List<User> users) {
final int batchSize = 50;
for (int i = 0; i < users.size(); i++) {
entityManager.persist(users.get(i));
if (i % batchSize == 0) {
// 定期刷新和清理持久化上下文
entityManager.flush();
entityManager.clear();
}
}
// 处理剩余的数据
entityManager.flush();
entityManager.clear();
}
}
读写分离配置
@Configuration
public class DataSourceConfig {
@Bean
@Primary
@ConfigurationProperties("spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@Primary
public DataSource routingDataSource() {
RoutingDataSource routingDataSource = new RoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put(DataSourceType.PRIMARY, primaryDataSource());
dataSourceMap.put(DataSourceType.SECONDARY, secondaryDataSource());
routingDataSource.setTargetDataSources(dataSourceMap);
routingDataSource.setDefaultTargetDataSource(primaryDataSource());
return routingDataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource routingDataSource) {
return new JdbcTemplate(routingDataSource);
}
}
// 读写分离注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnly {
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface WriteOnly {
}
// 数据源切换切面
@Aspect
@Component
public class DataSourceAspect {
@Before("@annotation(readOnly)")
public void setReadDataSource(ReadOnly readOnly) {
DataSourceContextHolder.setDataSourceType(DataSourceType.SECONDARY);
}
@Before("@annotation(writeOnly)")
public void setWriteDataSource(WriteOnly writeOnly) {
DataSourceContextHolder.setDataSourceType(DataSourceType.PRIMARY);
}
@After("@annotation(readOnly) || @annotation(writeOnly)")
public void clearDataSource() {
DataSourceContextHolder.clearDataSourceType();
}
}
// 数据源上下文持有者
public class DataSourceContextHolder {
private static final ThreadLocal<DataSourceType> contextHolder = new ThreadLocal<>();
public static void setDataSourceType(DataSourceType dataSourceType) {
contextHolder.set(dataSourceType);
}
public static DataSourceType getDataSourceType() {
return contextHolder.get();
}
public static void clearDataSourceType() {
contextHolder.remove();
}
}
10.5 网络性能优化
HTTP/2和压缩配置
server:
# 启用HTTP/2
http2:
enabled: true
# 压缩配置
compression:
enabled: true
mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json
min-response-size: 1024
# 连接优化
tomcat:
max-threads: 300
min-spare-threads: 20
max-connections: 10000
accept-count: 200
connection-timeout: 20000
max-http-post-size: 10MB
# SSL/TLS优化
ssl:
enabled: true
ciphers: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
protocols: TLSv1.2,TLSv1.3
RestTemplate优化配置
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
// 使用连接池
HttpClient client = HttpClientBuilder.create()
.setMaxConnTotal(200) // 最大连接数
.setMaxConnPerRoute(100) // 每个路由的最大连接数
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectTimeout(5000) // 连接超时
.setSocketTimeout(30000) // 读取超时
.build())
.build();
HttpComponentsClientHttpRequestFactory factory =
new HttpComponentsClientHttpRequestFactory(client);
factory.setConnectTimeout(5000);
factory.setReadTimeout(30000);
RestTemplate restTemplate = new RestTemplate(factory);
// 配置消息转换器
restTemplate.setMessageConverters(Arrays.asList(
new StringHttpMessageConverter(StandardCharsets.UTF_8),
new FormHttpMessageConverter(),
new MappingJackson2HttpMessageConverter()
));
return restTemplate;
}
@Bean
public WebClient webClient() {
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(
HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.responseTimeout(Duration.ofSeconds(30))
.compress(true) // 启用压缩
))
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(1024 * 1024)) // 1MB
.build();
}
}
异步处理优化
@Service
public class AsyncOptimizationService {
private final AsyncTaskExecutor taskExecutor;
private final WebClient webClient;
// 并行处理多个任务
public CompletableFuture<ResultDTO> processMultipleTasks(RequestDTO request) {
// 创建并行任务
CompletableFuture<UserData> userDataFuture = CompletableFuture.supplyAsync(
() -> fetchUserData(request.getUserId()), taskExecutor);
CompletableFuture<OrderData> orderDataFuture = CompletableFuture.supplyAsync(
() -> fetchOrderData(request.getOrderId()), taskExecutor);
CompletableFuture<PaymentData> paymentDataFuture = CompletableFuture.supplyAsync(
() -> fetchPaymentData(request.getPaymentId()), taskExecutor);
// 组合结果
return userDataFuture.thenCombine(orderDataFuture, (userData, orderData) ->
new CombinedData(userData, orderData))
.thenCombine(paymentDataFuture, (combinedData, paymentData) ->
ResultDTO.builder()
.userData(combinedData.getUserData())
.orderData(combinedData.getOrderData())
.paymentData(paymentData)
.build());
}
// 批量异步处理
public CompletableFuture<List<ProcessResult>> processBatch(List<ProcessRequest> requests) {
List<CompletableFuture<ProcessResult>> futures = requests.stream()
.map(request -> CompletableFuture.supplyAsync(
() -> processSingle(request), taskExecutor))
.collect(Collectors.toList());
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenApply(v -> futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList()));
}
// 带超时的异步调用
public CompletableFuture<ResponseData> callExternalApiWithTimeout(RequestData request) {
return webClient.post()
.uri("/api/external")
.bodyValue(request)
.retrieve()
.bodyToMono(ResponseData.class)
.timeout(Duration.ofSeconds(10)) // 设置超时
.retry(3) // 重试3次
.toFuture();
}
private UserData fetchUserData(Long userId) {
// 模拟耗时操作
try {
Thread.sleep(1000);
return new UserData(userId, "User" + userId);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Thread interrupted", e);
}
}
// 其他私有方法...
}
11. Spring Boot微服务实践
11.1 微服务架构设计
服务拆分策略
// 用户服务微服务
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableFeignClients
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
// 服务间通信配置
@Configuration
public class FeignConfig {
@Bean
public RequestInterceptor requestInterceptor() {
return template -> {
// 传递用户信息
String token = SecurityContextHolder.getContext()
.getAuthentication().getCredentials().toString();
template.header("Authorization", "Bearer " + token);
// 传递追踪ID
String traceId = MDC.get("traceId");
if (traceId != null) {
template.header("X-Trace-Id", traceId);
}
};
}
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.BASIC; // 请求和响应的基本信息
}
}
// Feign客户端示例
@FeignClient(name = "order-service",
configuration = FeignConfig.class,
fallback = OrderServiceFallback.class)
public interface OrderServiceClient {
@GetMapping("/api/orders/user/{userId}")
List<OrderDTO> getUserOrders(@PathVariable("userId") Long userId);
@PostMapping("/api/orders")
OrderDTO createOrder(@RequestBody OrderCreateRequest request);
@GetMapping("/api/orders/{orderId}")
OrderDTO getOrder(@PathVariable("orderId") Long orderId);
}
// 服务降级处理
@Component
public class OrderServiceFallback implements OrderServiceClient {
@Override
public List<OrderDTO> getUserOrders(Long userId) {
// 返回缓存数据或空列表
return Collections.emptyList();
}
@Override
public OrderDTO createOrder(OrderCreateRequest request) {
// 返回降级响应
throw new ServiceUnavailableException("Order service is currently unavailable");
}
@Override
public OrderDTO getOrder(Long orderId) {
// 返回降级响应
return OrderDTO.builder()
.id(orderId)
.status("SERVICE_UNAVAILABLE")
.build();
}
}
服务注册与发现
# application.yml - 用户服务配置
spring:
application:
name: user-service
# Eureka客户端配置
cloud:
inetutils:
preferred-networks: 192.168.1
eureka:
client:
service-url:
defaultZone: http://eureka-server:8761/eureka/
register-with-eureka: true
fetch-registry: true
registry-fetch-interval-seconds: 30
instance:
hostname: ${HOSTNAME:${spring.application.name}}
prefer-ip-address: true
lease-renewal-interval-in-seconds: 30
lease-expiration-duration-in-seconds: 90
metadata-map:
zone: zone1
version: ${project.version:1.0.0}
server:
port: ${PORT:8081}
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always
服务网关配置
// Spring Cloud Gateway配置
@SpringBootApplication
@EnableEurekaClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("user-service", r -> r
.path("/api/users/**")
.filters(f -> f
.stripPrefix(1) // 去除前缀
.requestRateLimiter(config -> config
.setRateLimiter(redisRateLimiter())
.setKeyResolver(userKeyResolver())
)
.circuitBreaker(config -> config
.setName("user-service")
.setFallbackUri("forward:/fallback/user")
)
)
.uri("lb://user-service"))
.route("order-service", r -> r
.path("/api/orders/**")
.filters(f -> f
.stripPrefix(1)
.retry(retryConfig -> retryConfig
.setRetries(3)
.setMethods(GET, POST)
.setBackoff(Duration.ofSeconds(1), Duration.ofSeconds(5), 2, true)
)
)
.uri("lb://order-service"))
.build();
}
@Bean
public RedisRateLimiter redisRateLimiter() {
return new RedisRateLimiter(10, 20, 1); // replenishRate, burstCapacity, requestedTokens
}
@Bean
public KeyResolver userKeyResolver() {
return exchange -> exchange.getRequest()
.getHeaders()
.getFirst("X-User-Id") != null
? Mono.just(exchange.getRequest().getHeaders().getFirst("X-User-Id"))
: Mono.just("anonymous");
}
}
// 全局过滤器
@Component
public class GlobalAuthFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 检查认证头
String authHeader = request.getHeaders().getFirst("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 验证token
String token = authHeader.substring(7);
if (!validateToken(token)) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.FORBIDDEN);
return response.setComplete();
}
// 添加用户信息到请求头
ServerHttpRequest modifiedRequest = exchange.getRequest().mutate()
.header("X-User-Id", extractUserId(token))
.header("X-User-Roles", extractUserRoles(token))
.build();
return chain.filter(exchange.mutate().request(modifiedRequest).build());
}
@Override
public int getOrder() {
return -100; // 高优先级
}
private boolean validateToken(String token) {
// JWT验证逻辑
return true; // 简化示例
}
private String extractUserId(String token) {
// 从token中提取用户ID
return "123"; // 简化示例
}
private String extractUserRoles(String token) {
// 从token中提取用户角色
return "USER,ADMIN"; // 简化示例
}
}
11.2 配置中心
Spring Cloud Config配置
// 配置中心服务端
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
// 配置中心客户端
@Configuration
public class ConfigClientConfig {
@Bean
@RefreshScope
public SomeConfigurableBean someConfigurableBean() {
return new SomeConfigurableBean();
}
}
@Component
@RefreshScope
public class DatabaseConfig {
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Bean
@RefreshScope
public DataSource dataSource() {
return DataSourceBuilder.create()
.url(url)
.username(username)
.password(password)
.build();
}
}
// 配置变更监听
@Component
public class ConfigChangeListener {
@EventListener
public void handleRefreshEvent(RefreshRemoteApplicationEvent event) {
System.out.println("配置已刷新: " + event.getDestination());
// 执行配置变更后的逻辑
reloadCaches();
resetConnections();
}
private void reloadCaches() {
// 重新加载缓存
}
private void resetConnections() {
// 重置数据库连接等
}
}
# 配置中心服务端配置
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://github.com/your-org/config-repo
search-paths: '{application}'
clone-on-start: true
default-label: main
force-pull: true
health:
repositories:
app-config:
label: main
name: app-config
profiles: dev,prod
server:
port: 8888
# 配置中心客户端配置
spring:
application:
name: user-service
cloud:
config:
uri: http://config-server:8888
label: main
profile: ${SPRING_PROFILES_ACTIVE:dev}
fail-fast: true
retry:
initial-interval: 1000
max-attempts: 6
max-interval: 8000
multiplier: 1.1
11.3 服务熔断和降级
Hystrix熔断器配置
@Service
public class OrderServiceWithCircuitBreaker {
private final OrderServiceClient orderServiceClient;
private final CacheManager cacheManager;
// 使用Hystrix实现熔断
@HystrixCommand(
fallbackMethod = "getOrdersFallback",
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"),
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50")
}
)
public List<OrderDTO> getUserOrders(Long userId) {
try {
return orderServiceClient.getUserOrders(userId);
} catch (Exception e) {
throw new ExternalServiceException("Failed to fetch orders", e);
}
}
// 熔断降级方法
public List<OrderDTO> getOrdersFallback(Long userId, Throwable throwable) {
// 1. 尝试从缓存获取
Cache cache = cacheManager.getCache("userOrders");
if (cache != null) {
Cache.ValueWrapper wrapper = cache.get(userId);
if (wrapper != null) {
return (List<OrderDTO>) wrapper.get();
}
}
// 2. 返回默认数据
return getDefaultOrders(userId);
}
private List<OrderDTO> getDefaultOrders(Long userId) {
// 返回默认或空数据
return Collections.emptyList();
}
}
// 熔断器监控配置
@Component
public class CircuitBreakerMonitor {
private final MeterRegistry meterRegistry;
public CircuitBreakerMonitor(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
registerHystrixMetrics();
}
private void registerHystrixMetrics() {
Metrics.registerGauge("hystrix.circuit.breaker.status",
Collections.emptyList(),
this,
CircuitBreakerMonitor::getCircuitBreakerStatus);
}
private double getCircuitBreakerStatus() {
// 获取熔断器状态
return 1.0; // 简化示例
}
@EventListener
public void handleCircuitBreakerEvent(HystrixCommandExecutionEvent event) {
if (event.getExecutionEventType() == HystrixEventType.FAILURE) {
meterRegistry.counter("hystrix.command.failure",
"command", event.getCommandName()).increment();
} else if (event.getExecutionEventType() == HystrixEventType.SUCCESS) {
meterRegistry.counter("hystrix.command.success",
"command", event.getCommandName()).increment();
}
}
}
Resilience4j熔断器配置
@Configuration
public class Resilience4jConfig {
@Bean
public CircuitBreaker orderServiceCircuitBreaker() {
return CircuitBreaker.ofDefaults("orderService");
}
@Bean
public Retry orderServiceRetry() {
return Retry.ofDefaults("orderService");
}
@Bean
public RateLimiter orderServiceRateLimiter() {
return RateLimiter.ofDefaults("orderService");
}
@Bean
public TimeLimiter orderServiceTimeLimiter() {
return TimeLimiter.of(Duration.ofSeconds(3));
}
}
@Service
public class OrderServiceWithResilience4j {
private final OrderServiceClient orderServiceClient;
private final CircuitBreaker circuitBreaker;
private final Retry retry;
private final RateLimiter rateLimiter;
public OrderServiceWithResilience4j(OrderServiceClient orderServiceClient,
CircuitBreaker circuitBreaker,
Retry retry,
RateLimiter rateLimiter) {
this.orderServiceClient = orderServiceClient;
this.circuitBreaker = circuitBreaker;
this.retry = retry;
this.rateLimiter = rateLimiter;
}
// 使用Resilience4j装饰器链
public List<OrderDTO> getUserOrders(Long userId) {
Supplier<List<OrderDTO>> supplier = () -> {
try {
return orderServiceClient.getUserOrders(userId);
} catch (Exception e) {
throw new ExternalServiceException("Failed to fetch orders", e);
}
};
Supplier<List<OrderDTO>> decoratedSupplier = Decorators.ofSupplier(supplier)
.withCircuitBreaker(circuitBreaker)
.withRetry(retry)
.withRateLimiter(rateLimiter)
.decorate();
try {
return decoratedSupplier.get();
} catch (Exception e) {
return getFallbackOrders(userId);
}
}
private List<OrderDTO> getFallbackOrders(Long userId) {
return Collections.emptyList();
}
// 异步版本
public CompletableFuture<List<OrderDTO>> getUserOrdersAsync(Long userId) {
Supplier<CompletableFuture<List<OrderDTO>>> supplier = () ->
CompletableFuture.supplyAsync(() -> {
try {
return orderServiceClient.getUserOrders(userId);
} catch (Exception e) {
throw new ExternalServiceException("Failed to fetch orders", e);
}
});
Supplier<CompletableFuture<List<OrderDTO>>> decoratedSupplier = Decorators.ofSupplier(supplier)
.withCircuitBreaker(circuitBreaker)
.withRetry(retry)
.withRateLimiter(rateLimiter)
.decorate();
try {
return decoratedSupplier.get();
} catch (Exception e) {
return CompletableFuture.completedFuture(getFallbackOrders(userId));
}
}
}
11.4 分布式追踪
Sleuth分布式追踪配置
# application.yml
spring:
sleuth:
sampler:
probability: 1.0 # 采样率100%,生产环境建议0.1
zipkin:
base-url: http://zipkin-server:9411
enabled: true
# 分布式链路追踪配置
application:
name: user-service
# 日志配置中添加追踪信息
logging:
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%X{traceId:-},%X{spanId:-}] [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%X{traceId:-},%X{spanId:-}] [%thread] %-5level %logger{36} - %msg%n"
自定义追踪
@Service
public class TracingService {
private final Tracer tracer;
private final SpanNamer spanNamer;
public TracingService(Tracer tracer, SpanNamer spanNamer) {
this.tracer = tracer;
this.spanNamer = spanNamer;
}
// 手动创建span
public OrderDTO processOrder(OrderRequest request) {
// 创建新的span
Span span = tracer.nextSpan().name("processOrder");
try (Tracer.SpanInScope ws = tracer.withSpan(span.start())) {
// 添加标签
span.tag("orderId", request.getOrderId().toString());
span.tag("userId", request.getUserId().toString());
span.tag("orderType", request.getType());
// 业务逻辑
OrderDTO order = createOrder(request);
processPayment(order);
sendNotification(order);
span.tag("orderStatus", order.getStatus());
return order;
} finally {
span.end();
}
}
// 使用注解创建span
@NewSpan("calculate-discount")
public BigDecimal calculateDiscount(@SpanTag("userId") Long userId,
@SpanTag("orderAmount") BigDecimal orderAmount) {
Span span = tracer.nextSpan().name("lookup-user-level");
try (Tracer.SpanInScope ws = tracer.withSpan(span.start())) {
// 查询用户等级
UserLevel level = getUserLevel(userId);
span.tag("userLevel", level.name());
// 计算折扣
return calculateDiscountByLevel(orderAmount, level);
} finally {
span.end();
}
}
// 异步追踪
@Async
@NewSpan("send-notification")
public CompletableFuture<Void> sendNotificationAsync(@SpanTag("orderId") Long orderId) {
Span span = tracer.nextSpan().name("lookup-notification-preference");
try (Tracer.SpanInScope ws = tracer.withSpan(span.start())) {
NotificationPreference preference = getNotificationPreference(orderId);
span.tag("preference", preference.getType());
// 发送通知
sendNotification(orderId, preference);
return CompletableFuture.completedFuture(null);
} finally {
span.end();
}
}
// 跨服务传播
public void callExternalService(ExternalServiceRequest request) {
// 传播当前trace context
Span span = tracer.nextSpan().name("call-external-service");
try (Tracer.SpanInScope ws = tracer.withSpan(span.start())) {
// 将trace信息添加到HTTP头
String traceId = span.context().traceId();
String spanId = span.context().spanId();
HttpHeaders headers = new HttpHeaders();
headers.set("X-Trace-Id", traceId);
headers.set("X-Span-Id", spanId);
// 发起HTTP请求
restTemplate.exchange(
"http://external-service/api/data",
HttpMethod.POST,
new HttpEntity<>(request, headers),
ExternalServiceResponse.class
);
} finally {
span.end();
}
}
// 自定义span事件
public OrderDTO processComplexOrder(OrderRequest request) {
Span span = tracer.nextSpan().name("process-complex-order");
try (Tracer.SpanInScope ws = tracer.withSpan(span.start())) {
span.tag("complexity", "high");
// 添加自定义事件
span.event("order-validation-start");
validateOrder(request);
span.event("order-validation-complete");
span.event("inventory-check-start");
checkInventory(request);
span.event("inventory-check-complete");
span.event("payment-processing-start");
processPayment(request);
span.event("payment-processing-complete");
span.event("order-fulfillment-start");
OrderDTO result = fulfillOrder(request);
span.event("order-fulfillment-complete");
span.tag("result", "success");
return result;
} catch (Exception e) {
span.tag("error", e.getMessage());
span.tag("result", "failure");
throw e;
} finally {
span.end();
}
}
private UserLevel getUserLevel(Long userId) {
// 查询用户等级的实现
return UserLevel.GOLD;
}
private BigDecimal calculateDiscountByLevel(BigDecimal orderAmount, UserLevel level) {
// 根据用户等级计算折扣
return orderAmount.multiply(BigDecimal.valueOf(0.1));
}
// 其他辅助方法...
}
11.5 微服务监控
服务健康检查
@Component
public class CustomHealthIndicator implements HealthIndicator {
private final DiscoveryClient discoveryClient;
private final RestTemplate restTemplate;
@Override
public Health health() {
try {
// 检查服务发现状态
List<String> services = discoveryClient.getServices();
Map<String, Object> details = new HashMap<>();
details.put("discovery-services", services.size());
// 检查关键依赖服务
boolean databaseHealthy = checkDatabaseHealth();
boolean redisHealthy = checkRedisHealth();
boolean externalServiceHealthy = checkExternalServiceHealth();
details.put("database", databaseHealthy ? "UP" : "DOWN");
details.put("redis", redisHealthy ? "UP" : "DOWN");
details.put("external-service", externalServiceHealthy ? "UP" : "DOWN");
// 综合健康状态判断
if (databaseHealthy && redisHealthy) {
return Health.up()
.withDetails(details)
.build();
} else {
return Health.down()
.withDetails(details)
.build();
}
} catch (Exception e) {
return Health.down()
.withDetail("error", e.getMessage())
.withException(e)
.build();
}
}
private boolean checkDatabaseHealth() {
try {
// 执行简单的数据库查询
return true; // 简化示例
} catch (Exception e) {
return false;
}
}
private boolean checkRedisHealth() {
try {
// 检查Redis连接
return true; // 简化示例
} catch (Exception e) {
return false;
}
}
private boolean checkExternalServiceHealth() {
try {
restTemplate.getForObject("http://external-service/actuator/health", String.class);
return true;
} catch (Exception e) {
return false;
}
}
}
// 服务级别健康检查
@Component
public class UserServiceHealthIndicator implements HealthIndicator {
private final UserService userService;
private final CacheManager cacheManager;
@Override
public Health health() {
try {
Map<String, Object> details = new HashMap<>();
// 检查用户服务核心功能
long userCount = userService.getTotalUserCount();
details.put("total-users", userCount);
// 检查缓存状态
Cache userCache = cacheManager.getCache("users");
details.put("cache-available", userCache != null);
// 检查数据库连接池状态
details.put("database-connections", getDatabaseConnectionCount());
// 性能指标
details.put("avg-response-time", getAverageResponseTime());
details.put("error-rate", getErrorRate());
if (userCount > 0 && getErrorRate() < 0.05) { // 错误率小于5%
return Health.up()
.withDetails(details)
.build();
} else {
return Health.down()
.withDetails(details)
.build();
}
} catch (Exception e) {
return Health.down()
.withDetail("error", e.getMessage())
.build();
}
}
private int getDatabaseConnectionCount() {
// 获取数据库连接数
return 10; // 简化示例
}
private double getAverageResponseTime() {
// 获取平均响应时间
return 150.0; // 简化示例
}
private double getErrorRate() {
// 获取错误率
return 0.02; // 简化示例
}
}
12. 经典面试题与实战案例
12.1 Spring Boot核心机制面试题
面试题1:Spring Boot自动配置原理
问题:请详细解释Spring Boot的自动配置是如何工作的?
答案:
Spring Boot自动配置通过以下机制工作:
1. 启动时通过@EnableAutoConfiguration注解启用自动配置
2. SpringFactoriesLoader扫描所有jar包中的META-INF/spring.factories文件
3. 加载EnableAutoConfiguration对应的自动配置类
4. 根据条件注解(@ConditionalOnClass、@ConditionalOnProperty等)判断是否创建Bean
5. 按照优先级应用配置:用户配置 > 自动配置 > 默认配置
代码演示:
// 自动配置类示例
@Configuration
@ConditionalOnClass(DataSource.class) // 类路径中存在DataSource类
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean // 容器中没有DataSource Bean时才创建
public DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}
// spring.factories文件内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
面试题2:Spring Boot启动流程
问题:请描述Spring Boot应用的启动流程和关键步骤?
答案:
Spring Boot启动流程:
1. 创建SpringApplication对象
2. 推断应用类型(Web应用或普通应用)
3. 加载ApplicationContextInitializer
4. 加载ApplicationListener
5. 推断主启动类
6. 调用run()方法:
- 创建ApplicationContext
- 准备环境(Environment)
- 打印Banner
- 创建上下文
- 准备上下文(load sources, refresh context)
- 刷新上下文
- 调用Runner接口
面试题3:Spring Boot与Spring Framework的关系
问题:Spring Boot和Spring Framework有什么关系和区别?
答案:
关系:
- Spring Boot基于Spring Framework构建
- Spring Boot简化了Spring应用的开发和配置
- Spring Boot不是对Spring Framework的替代,而是增强
区别:
1. 配置方式:
- Spring Framework:需要大量XML或Java配置
- Spring Boot:通过自动配置减少配置
2. 依赖管理:
- Spring Framework:需要手动管理依赖版本
- Spring Boot:通过starter管理依赖
3. 部署方式:
- Spring Framework:需要外部服务器
- Spring Boot:内嵌服务器,可独立运行
4. 开发效率:
- Spring Framework:配置复杂,学习曲线陡峭
- Spring Boot:约定优于配置,开箱即用
12.2 配置和管理面试题
面试题4:配置文件优先级和热更新
问题:Spring Boot配置文件的加载优先级是怎样的?如何实现配置热更新?
答案:
配置文件加载优先级(从高到低):
1. 命令行参数
2. Java系统属性(System.getProperties())
3. 操作系统环境变量
4. 外部配置文件(config/application.properties)
5. 外部配置文件(application.properties)
6. 内部配置文件(config/application.properties)
7. 内部配置文件(application.properties)
8. @PropertySource注解
9. 默认属性
配置热更新实现方式:
1. 使用Spring Cloud Config Server
2. 结合@RefreshScope注解
3. 通过actuator的/refresh端点
代码示例:
@RestController
@RefreshScope // 支持配置热更新
public class ConfigController {
@Value("${app.message:default}")
private String message;
@GetMapping("/config")
public String getConfig() {
return message;
}
}
// 使用@ConfigurationProperties实现类型安全的配置绑定
@ConfigurationProperties(prefix = "app")
@RefreshScope
@Component
public class AppConfig {
private String message;
private int timeout;
// getters and setters
}
面试题5:多环境配置管理
问题:如何在Spring Boot中管理多环境配置?
答案:
# application.yml - 主配置文件
spring:
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}
application:
name: my-service
---
spring:
config:
activate:
on-profile: dev
server:
port: 8080
logging:
level:
root: DEBUG
app:
datasource:
url: jdbc:mysql://localhost:3306/dev_db
---
spring:
config:
activate:
on-profile: prod
server:
port: 80
logging:
level:
root: INFO
app:
datasource:
url: jdbc:mysql://prod-db:3306/prod_db
12.3 性能优化面试题
面试题6:Spring Boot性能优化策略
问题:你会从哪些方面对Spring Boot应用进行性能优化?
答案:
Spring Boot性能优化策略:
1. JVM层面优化:
- 调整堆内存大小(-Xms, -Xmx)
- 选择合适的垃圾回收器(G1, ZGC)
- 启用JIT编译优化
2. 应用层面优化:
- 减少不必要的Bean创建
- 使用@Lazy延迟初始化
- 合理使用缓存(@Cacheable)
- 优化数据库连接池配置
3. 服务器层面优化:
- 调整Tomcat线程池参数
- 启用HTTP/2支持
- 配置压缩和缓存
4. 代码层面优化:
- 避免N+1查询问题
- 使用异步处理(@Async, CompletableFuture)
- 合理使用事务
配置示例:
# 服务器优化配置
server:
tomcat:
max-threads: 300
min-spare-threads: 20
max-connections: 10000
accept-count: 200
connection-timeout: 20000
# 数据库连接池优化
spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
idle-timeout: 300000
max-lifetime: 1200000
connection-timeout: 20000
# 缓存配置
spring:
cache:
type: redis
redis:
time-to-live: 600000 # 10分钟
12.4 实战案例
案例1:自定义Starter开发
场景:开发一个短信服务的Spring Boot Starter
实现:
// 1. 配置属性类
@ConfigurationProperties(prefix = "sms.service")
public class SmsProperties {
private String provider = "aliyun";
private String accessKey;
private String secretKey;
private String signName;
private Map<String, String> templates = new HashMap<>();
// getters and setters
}
// 2. 短信服务接口
public interface SmsService {
boolean sendSms(String phone, String templateCode, Map<String, String> params);
}
// 3. 阿里云短信服务实现
public class AliyunSmsServiceImpl implements SmsService {
private final SmsProperties properties;
@Override
public boolean sendSms(String phone, String templateCode, Map<String, String> params) {
// 实现阿里云短信发送逻辑
return true;
}
}
// 4. 自动配置类
@Configuration
@ConditionalOnClass(SmsService.class)
@EnableConfigurationProperties(SmsProperties.class)
public class SmsAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "sms.service", name = "enabled", havingValue = "true")
public SmsService smsService(SmsProperties properties) {
if ("aliyun".equals(properties.getProvider())) {
return new AliyunSmsServiceImpl(properties);
}
throw new IllegalStateException("Unsupported SMS provider: " + properties.getProvider());
}
}
案例2:实现优雅停机
场景:确保应用在关闭时完成正在处理的请求
实现:
# 启用优雅停机
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
@Component
public class GracefulShutdownManager {
@PreDestroy
public void shutdown() {
System.out.println("Application is shutting down gracefully...");
// 执行清理逻辑
}
}
// 实现SmartLifecycle管理组件生命周期
@Component
public class CustomLifecycleManager implements SmartLifecycle {
private volatile boolean running = false;
@Override
public void start() {
running = true;
System.out.println("Custom component started");
}
@Override
public void stop() {
running = false;
System.out.println("Custom component stopped gracefully");
}
@Override
public boolean isRunning() {
return running;
}
@Override
public int getPhase() {
return 0;
}
}
12.5 高级面试题
面试题7:Spring Boot中的循环依赖问题
问题:如何在Spring Boot中解决循环依赖问题?
答案:
解决循环依赖的方法:
1. 构造器注入 vs Field注入:
- 构造器注入更容易发现循环依赖
- Field注入可能掩盖循环依赖问题
2. 使用@Lazy注解延迟初始化
3. 重构代码,消除循环依赖
4. 使用ApplicationContextAware获取依赖
最佳实践:
- 优先使用构造器注入
- 合理设计类之间的依赖关系
- 使用事件机制替代直接依赖
面试题8:Spring Boot测试策略
问题:如何为Spring Boot应用设计完整的测试策略?
答案:
Spring Boot测试策略:
1. 单元测试(@SpringBootTest未启用)
- 使用@ExtendWith(MockitoExtension.class)
- 测试单个类的方法
2. 集成测试
- @SpringBootTest:启动整个应用上下文
- @WebMvcTest:测试Web层
- @DataJpaTest:测试数据访问层
3. 测试配置
- 使用test application.properties
- 使用@ActiveProfiles指定测试环境
- 使用@MockBean模拟依赖
4. 测试最佳实践
- 快速反馈:单元测试优先
- 隔离性:测试之间不相互影响
- 覆盖率:确保关键代码被测试
📚 总结
本文档详细解析了Spring Boot框架的核心概念、工作机制和实战应用,涵盖了从基础配置到高级特性的全面内容。通过结合经典面试题和实际案例,帮助开发者深入理解Spring Boot的设计理念和最佳实践。
核心要点回顾
- 自动配置机制:通过条件注解和spring.factories实现智能配置
- Starter依赖:简化依赖管理,提供开箱即用的功能模块
- 配置体系:支持多环境配置、类型安全绑定和配置热更新
- 嵌入式服务器:支持Tomcat、Jetty、Undertow,并提供详细配置
- 生命周期管理:理解应用启动流程和优雅停机机制
学习建议
- 理论联系实际:通过编写代码加深对概念的理解
- 阅读源码:深入理解Spring Boot的实现原理
- 实践项目:在实际项目中应用所学知识
- 关注社区:跟进Spring Boot的最新发展和最佳实践
面试准备
重点掌握:
- Spring Boot与Spring Framework的关系
- 自动配置原理和实现机制
- 常用注解的使用场景和原理
- 配置管理和环境切换
- 性能优化和问题排查
通过本文档的学习,相信你能更好地掌握Spring Boot框架,在实际开发和面试中游刃有余。