在 Spring Cloud Gateway 中结合 MyBatis 动态从数据库加载路由配置,可以实现灵活的路由管理。以下是详细实现步骤:
1. 数据库表设计
创建路由配置表 gateway_route
:
CREATE TABLE `gateway_route` (
`id` varchar(50) NOT NULL COMMENT '路由唯一标识',
`uri` varchar(200) NOT NULL COMMENT '目标服务地址(如 lb://user-service)',
`predicates` text COMMENT '路由断言(JSON 数组格式)',
`filters` text COMMENT '路由过滤器(JSON 数组格式)',
`order` int(11) DEFAULT '0' COMMENT '路由优先级',
`enabled` tinyint(1) DEFAULT '1' COMMENT '是否启用',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
路由配置字段说明
字段名 | 值 | 含义 |
---|---|---|
id |
user_route |
路由的唯一标识符,用于区分不同路由规则 |
uri |
http://127.0.0.1:8081 |
目标服务地址,请求将被转发到此地址 |
predicates |
[{"name":"Path","args":{"pattern":"/test/**"}}] |
路由断言规则,匹配请求路径 |
filters |
[{"name":"StripPrefix","args":{"parts":"1"}}] |
路由过滤器,修改请求路径 |
order |
0 |
路由优先级(数值越小优先级越高) |
enabled |
1 |
是否启用该路由(1-启用,0-禁用) |
示例数据:
INSERT INTO `gateway_route`
VALUES ('user_route', 'lb://user-service',
'[{"name":"Path","args":{"pattern":"/api/users/**"}}]',
'[{"name":"StripPrefix","args":{"parts":"1"}}]',
0, 1);
2. 添加依赖
在 pom.xml
中添加以下依赖:
<!-- Spring Cloud Gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- MyBatis 整合 Spring Boot -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- JSON 处理工具 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
3. 实体类与 Mapper
实体类 GatewayRoute
public class GatewayRoute {
private String id;
private String uri;
private String predicates; // JSON 字符串,如 [{"name":"Path", "args":{"pattern":"/api/**"}}]
private String filters; // JSON 字符串,如 [{"name":"StripPrefix", "args":{"parts":"1"}}]
private int order;
private boolean enabled;
// Getters & Setters
}
Mapper 接口
@Mapper
public interface RouteMapper {
@Select("SELECT * FROM gateway_route WHERE enabled = 1")
List<GatewayRoute> findAllEnabledRoutes();
}
4. 动态路由加载实现
自定义路由仓库
@Component
public class DbRouteDefinitionRepository implements RouteDefinitionLocator {
private final RouteMapper routeMapper;
private final ObjectMapper objectMapper;
public DbRouteDefinitionRepository(RouteMapper routeMapper, ObjectMapper objectMapper) {
this.routeMapper = routeMapper;
this.objectMapper = objectMapper;
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
List<GatewayRoute> routes = routeMapper.findAllEnabledRoutes();
return Flux.fromIterable(routes)
.map(this::convertToRouteDefinition);
}
private RouteDefinition convertToRouteDefinition(GatewayRoute route) {
RouteDefinition definition = new RouteDefinition();
definition.setId(route.getId());
definition.setUri(URI.create(route.getUri()));
definition.setOrder(route.getOrder());
try {
// 解析 Predicates
List<PredicateDefinition> predicates = objectMapper.readValue(
route.getPredicates(),
new TypeReference<List<PredicateDefinition>>() {}
);
definition.setPredicates(predicates);
// 解析 Filters
List<FilterDefinition> filters = objectMapper.readValue(
route.getFilters(),
new TypeReference<List<FilterDefinition>>() {}
);
definition.setFilters(filters);
} catch (JsonProcessingException e) {
throw new RuntimeException("路由配置解析失败: " + route.getId(), e);
}
return definition;
}
}
5. 配置 MyBatis 和数据源
application.yml
配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/gateway_db?useSSL=false&characterEncoding=utf8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
# MyBatis 配置
mybatis:
type-aliases-package: com.example.gateway.entity # 实体类包路径
configuration:
map-underscore-to-camel-case: true # 自动驼峰命名转换
启动类添加 Mapper 扫描
@SpringBootApplication
@MapperScan("com.example.gateway.mapper") // 指定 Mapper 接口所在包
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
6. 动态路由刷新接口
添加一个触发路由刷新的接口:
@RestController
@RequestMapping("/gateway")
public class RouteRefreshController {
private final ApplicationEventPublisher eventPublisher;
public RouteRefreshController(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
@PostMapping("/refresh")
public String refreshRoutes() {
eventPublisher.publishEvent(new RefreshRoutesEvent(this));
return "路由刷新成功";
}
}
7. 测试验证
启动应用:确保数据库连接正常且表数据存在。
触发路由刷新:
curl -X POST http://localhost:8080/gateway/refresh
验证路由转发:
curl http://localhost:8080/api/users/1
请求应被转发到
user-service
服务。
INSERT INTO gateway.gateway_route (id,uri,predicates,filters,`order`,enabled) VALUES
('user_route','http://127.0.0.1:8081','[{"name":"Path","args":{"pattern":"/test/**"}}]','[{"name":"StripPrefix","args":{"parts":"1"}}]',0,1);
我启动的是普通的springboot项目,端口是8081
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping(value = "/echo/{string}")
public String echo(@PathVariable String string) {
return string;
}
}
8. 动态更新流程
修改数据库路由数据:通过 SQL 或管理界面更新路由配置。
调用刷新接口:
curl -X POST http://localhost:8080/gateway/refresh
观察日志:检查新路由是否加载成功。
常见问题与解决
问题 1:MyBatis Mapper 未注入
错误信息:
No qualifying bean of type 'com.example.gateway.mapper.RouteMapper'
解决:
确认
@MapperScan
注解指定了正确的包路径。检查 Mapper 接口是否有
@Mapper
注解。
问题 2:JSON 解析失败
错误信息:
路由配置解析失败
解决:
检查数据库中的
predicates
和filters
字段是否符合 JSON 格式。使用 JSON 校验工具验证字段内容。
问题 3:路由未生效
排查步骤:
确认数据库中的
enabled
字段为1
。检查
uri
格式是否正确(如lb://service-name
需确保服务发现已启用)。
9. 扩展优化
添加缓存
减少数据库频繁查询:
@Component
public class CachedRouteDefinitionRepository implements RouteDefinitionLocator {
private final RouteDefinitionLocator delegate;
private final Cache<String, RouteDefinition> cache = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
public CachedRouteDefinitionRepository(RouteDefinitionLocator delegate) {
this.delegate = delegate;
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
return delegate.getRouteDefinitions()
.doOnNext(route -> cache.put(route.getId(), route));
}
public RouteDefinition getRoute(String id) {
return cache.getIfPresent(id);
}
}
集成服务发现
在 uri
中使用 lb://
格式时,确保已启用服务发现:
spring:
cloud:
discovery:
enabled: true
通过以上步骤,即可实现基于 MyBatis 的 Spring Cloud Gateway 动态路由管理,支持通过数据库灵活配置和实时更新路由规则。