SpringBoot进阶
开篇:SpringBoot 还能做什么?
掌握了 SpringBoot 的基础之后,你会发现日常开发中还有很多"进阶需求":项目需要读写分离怎么动态切换数据源?应用上线后怎么监控它的健康状况?服务重启时怎么保证正在处理的请求不被打断?
这篇文章聚焦 SpringBoot 的进阶能力,从动态数据源到优雅停机,帮你把 SpringBoot 用到位。
一、动态数据源
为什么需要动态数据源?
在中大型项目中,数据库通常会做读写分离——写操作走主库,读操作走从库。或者在多租户场景下,不同租户的数据存在不同的数据库中。这些场景都需要在运行时动态切换数据源。
核心原理:AbstractRoutingDataSource
Spring 提供了一个抽象类 AbstractRoutingDataSource,它的工作原理就像一个数据源路由器:
你只需要实现 determineCurrentLookupKey() 方法,告诉路由器当前应该使用哪个数据源。
实现步骤
整个实现分为六步:
第一步:自定义注解
定义一个注解来标记方法或类使用哪个数据源:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface DataSource {
String value() default "master";
}第二步:ThreadLocal 存储当前数据源名称
用 ThreadLocal 来保存每个线程当前应该使用的数据源:
public class DataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();
public static void set(String dsName) { CONTEXT.set(dsName); }
public static String get() { return CONTEXT.get(); }
public static void clear() { CONTEXT.remove(); }
}第三步:AOP 切面拦截注解
用切面来拦截 @DataSource 注解,在方法执行前设置数据源,执行后清除:
@Aspect
@Component
public class DataSourceAspect {
@Pointcut("@annotation(com.example.annotation.DataSource) " +
"|| @within(com.example.annotation.DataSource)")
public void dsPointcut() {}
@Around("dsPointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
DataSource ds = getDataSource(pjp);
if (ds != null) {
DataSourceContextHolder.set(ds.value());
}
try {
return pjp.proceed();
} finally {
DataSourceContextHolder.clear(); // 一定要清除,避免线程池复用导致脏数据
}
}
private DataSource getDataSource(ProceedingJoinPoint pjp) {
MethodSignature sig = (MethodSignature) pjp.getSignature();
// 方法上的注解优先级高于类上的
DataSource ds = AnnotationUtils.findAnnotation(sig.getMethod(), DataSource.class);
if (ds != null) return ds;
return AnnotationUtils.findAnnotation(sig.getDeclaringType(), DataSource.class);
}
}第四步:继承 AbstractRoutingDataSource
@Component
public class DynamicDataSource extends AbstractRoutingDataSource {
public DynamicDataSource(Map<String, javax.sql.DataSource> dataSources) {
// 设置所有可用的数据源
super.setTargetDataSources(new HashMap<>(dataSources));
// 设置默认数据源
super.setDefaultTargetDataSource(dataSources.get("master"));
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.get();
}
}第五步:配置多个数据源
spring:
datasource:
ds:
master:
url: jdbc:mysql://localhost:3306/main_db
username: root
password: 123456
slave:
url: jdbc:mysql://localhost:3306/slave_db
username: root
password: 123456第六步:使用
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
// 默认走主库
public void createUser(User user) {
userMapper.insert(user);
}
// 标注注解,走从库
@DataSource("slave")
public List<User> queryUsers() {
return userMapper.selectAll();
}
}注意事项
- ThreadLocal 一定要在 finally 中 clear,否则线程池复用时会残留脏数据
@DataSource注解的方法不能和@Transactional同一个类中自调用,原因和事务失效一样——AOP 代理不生效- 如果项目中已有成熟的中间件(如
dynamic-datasource-spring-boot-starter),推荐直接使用
二、Actuator 监控
什么是 Actuator?
Actuator 是 SpringBoot 提供的应用监控模块,可以暴露应用的运行时信息,比如健康状态、内存使用、Bean 列表、配置属性等。
快速上手
引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>配置暴露端点:
management:
endpoints:
web:
exposure:
include: "*" # 暴露所有端点(生产环境请按需暴露)
endpoint:
health:
show-details: always # 显示健康检查详细信息启动应用后,访问 http://localhost:8080/actuator 可以看到所有可用的端点。
常用端点
| 端点 | 路径 | 作用 |
|---|---|---|
| health | /actuator/health | 应用健康状态(数据库、Redis、磁盘等) |
| info | /actuator/info | 应用基本信息 |
| metrics | /actuator/metrics | 各种度量指标(JVM、HTTP 请求等) |
| beans | /actuator/beans | 容器中所有 Bean 的列表 |
| env | /actuator/env | 环境属性和配置信息 |
| loggers | /actuator/loggers | 查看和修改日志级别 |
| shutdown | /actuator/shutdown | 优雅停机(需要额外开启) |
自定义健康检查
你可以实现 HealthIndicator 接口来添加自定义的健康检查:
@Component
public class ThirdPartyServiceHealthIndicator implements HealthIndicator {
@Override
public Health health() {
try {
// 检查第三方服务是否可用
boolean isAvailable = checkThirdPartyService();
if (isAvailable) {
return Health.up().withDetail("service", "available").build();
}
return Health.down().withDetail("service", "unavailable").build();
} catch (Exception e) {
return Health.down(e).build();
}
}
}自定义端点
除了使用内置端点,你还可以创建自己的端点:
@Component
@Endpoint(id = "app-info")
public class AppInfoEndpoint {
@ReadOperation
public Map<String, Object> info() {
Map<String, Object> info = new HashMap<>();
info.put("version", "1.0.0");
info.put("buildTime", "2024-01-01");
info.put("author", "team");
return info;
}
}访问 /actuator/app-info 即可看到自定义信息。
三、优雅停机
问题场景
假设你的应用正在处理一个重要的订单请求,这时候运维执行了服务重启。如果直接 kill -9,这个请求就被中断了——订单可能只扣了钱但没创建成功。
优雅停机就是为了解决这个问题:停机时不再接收新请求,但等待正在处理的请求完成后再关闭。
SpringBoot 内置优雅停机
从 SpringBoot 2.3 开始,只需要一行配置:
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30s # 最长等待时间,默认 30 秒启用后,当收到停机信号时:
- 不再接收新的请求
- 等待正在处理的请求完成(最多等待配置的超时时间)
- 超时后强制关闭
通过 Actuator 触发停机
也可以通过 HTTP 请求触发优雅停机:
management:
endpoint:
shutdown:
enabled: truecurl -X POST http://localhost:8080/actuator/shutdown生产环境安全
shutdown 端点在生产环境中必须加上安全认证,否则任何人都可以通过一个 POST 请求关闭你的应用。
四、SpringBoot Admin
是什么?
当你有几十个 SpringBoot 微服务需要监控时,一个一个去访问 Actuator 端点显然不现实。SpringBoot Admin 是一个开源的可视化监控面板,可以集中展示所有服务的运行状态。
架构
每个应用作为 Admin Client 注册到 Admin Server,Admin Server 定期拉取各个应用的 Actuator 数据并展示。
搭建 Admin Server
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>启动类上加 @EnableAdminServer。
注册 Admin Client
在你的业务应用中引入客户端依赖:
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>配置 Admin Server 地址:
spring:
boot:
admin:
client:
url: http://admin-server:8080启动后,在 Admin Server 的面板上就能看到应用的健康状态、内存使用、日志级别等信息。
五、常见面试题精选
1. SpringBoot 如何实现优雅停机?
从 SpringBoot 2.3 开始,配置 server.shutdown=graceful 即可。停机时不再接收新请求,等待正在处理的请求完成(最多等待配置的超时时间),然后关闭。也可以通过 Actuator 的 shutdown 端点以 HTTP POST 方式触发。
2. 如何实现动态数据源切换?
核心是继承 AbstractRoutingDataSource,重写 determineCurrentLookupKey() 方法,配合 ThreadLocal 存储当前线程要使用的数据源名称。通过自定义 @DataSource 注解和 AOP 切面,在方法执行前设置数据源,执行后清除。
3. Actuator 有哪些常用端点?
/health 查看应用健康状态,/metrics 查看度量指标,/beans 列出所有 Bean,/env 查看环境配置,/loggers 查看和动态修改日志级别。可以通过实现 HealthIndicator 或 @Endpoint 来自定义端点。
4. SpringBoot Admin 是什么?
一个开源的可视化监控面板,用于集中管理和监控多个 SpringBoot 应用。各应用通过 Actuator 暴露数据,Admin Client 注册到 Admin Server,Server 统一拉取并展示各应用的运行状态、内存使用、日志级别等信息。
小结
SpringBoot 的进阶能力让你的应用从"能跑"到"跑得好":
- 动态数据源:通过
AbstractRoutingDataSource+ ThreadLocal + AOP,实现运行时数据源切换,支撑读写分离和多租户场景 - Actuator 监控:内置丰富的监控端点,支持自定义健康检查和端点扩展
- 优雅停机:一行配置保证服务重启时不丢失正在处理的请求
- Admin 面板:集中管理多个服务的监控数据,告别逐个检查
这些能力在日常开发中可能不是每天都用,但一旦需要,就能让你的应用质量上一个台阶。