spring boot 全局异常处理可以通过在 @controlleradvice 中定义针对 org.springframework.jdbc.datasource.lookup.datasourcelookupexception 或具体实现类(如 abstractdatasourcelookup 抛出的异常)的 @exceptionhandler 方法来捕获数据源切换异常。当使用动态数据源路由时,若指定的数据源标识不存在或配置错误,框架会抛出此类异常,此时全局处理器可拦截该异常并返回统一的错误响应。

如何配置全局异常处理器以拦截数据源查找失败
要拦截数据源切换异常,需要在配置了动态数据源的路由策略后,编写一个继承自 ResponseEntityExceptionHandler 或直接标注 @RestControllerAdvice 的类。在该类中,添加一个方法并使用 @ExceptionHandler(DataSourceLookupException.class) 注解。当应用尝试切换到未注册的数据源时,底层路由机制会抛出此异常,该方法将捕获它并返回 HTTP 404 或 500 状态码及自定义消息。

import org.springframework.jdbc.datasource.lookup.DataSourceLookupException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(DataSourceLookupException.class)
public ResponseEntity<string> handleDataSourceNotFound(DataSourceLookupException ex) {
return ResponseEntity.status(404).body("Data source not found: " + ex.getMessage());
}
}</string>
上述代码定义了一个全局异常处理器。当 DynamicRoutingDataSource 无法通过 resolveDataSource 找到对应的 DataSource 实例时,它会抛出 DataSourceLookupException。此处理器捕获该异常,提取消息内容,并以 JSON 字符串形式返回给客户端。确保项目中已引入 spring-boot-starter-jdbc 和动态数据源依赖(如 dynamic-datasource-spring-boot-starter 或自定义实现)。
数据源切换异常的触发条件与路由逻辑
数据源切换异常通常在调用 DataSourceContextHolder 设置非默认或非已注册的数据源键值时触发。例如,在多线程环境下未正确清除上下文,或者前端传入的数据源标识在配置列表中不存在。路由逻辑依赖于 AbstractRoutingDataSource 的 determineTargetDataSource 方法,该方法查询当前线程绑定的键值,并在内部映射表中查找对应的 DataSource。若找不到,则根据配置决定是抛出异常还是使用默认数据源。

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final Map<object object> targetDataSources = new HashMap();
static {
// 模拟初始化时注册的数据源
targetDataSources.put("master", dataSourceMaster);
targetDataSources.put("slave", dataSourceSlave);
}
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
public static void setTargetDataSources(Map<object object> newTargets) {
targetDataSources.putAll(newTargets);
super.setTargetDataSources(targetDataSources);
}
}</object></object>
这段代码展示了自定义动态数据源的核心部分。它重写了 determineCurrentLookupKey 以从上下文获取当前请求的数据源键。如果 DataSourceContextHolder 中设置的键(如 "unknown_db")不在 targetDataSources 映射中,且未设置 defaultTargetDataSource,则父类 AbstractRoutingDataSource 将抛出 IllegalStateException 或其子类异常。若使用了第三方库如 dynamic-datasource-spring-boot-starter,它通常会在找不到数据源时抛出 DataSourceLookupException,这正是前文异常处理器所针对的目标。
当数据源键为空时会发生什么
当 DataSourceContextHolder 中的数据源键为 null 或未设置时,AbstractRoutingDataSource 的行为取决于是否配置了默认数据源。如果配置了 defaultTargetDataSource,它将回退使用该默认数据源,不会抛出异常。如果未配置默认数据源,则会抛出 IllegalStateException,提示 "Cannot determine target DataSource for lookup key [null]"。因此,在全局异常处理中,除了捕获 DataSourceLookupException,还应考虑捕获 IllegalStateException 以处理空键情况。











