1. 引言
在之前的文章中,我们已经介绍过 Spring Cloud Gateway 的基本概念,以及如何使用内置的断言(Predicate)来实现简单的路由规则。但在实际项目中,仅靠内置断言往往不够用。比如你的路由逻辑需要查数据库、调用外部服务,或者依赖复杂的业务判断。
这时候,Spring Cloud Gateway 支持我们自定义断言工厂(Custom Predicate Factory)。一旦实现,就可以像使用内置断言一样,在路由配置中通过流畅 API 或 YAML 配置自由使用。
✅ 自定义断言 = 更灵活的路由控制
❌ 盲目使用可能导致性能瓶颈(比如频繁查 DB)
2. 断言的结构解析
简单来说,Spring Cloud Gateway 中的 Predicate 就是一个判断请求是否满足条件的函数。每个路由可以配置一个或多个断言,只有全部匹配,才会将请求转发到后端服务(并应用过滤器)。
要实现自定义断言,我们得先看看官方是怎么设计的。核心套路是使用 工厂方法模式(Factory Method Pattern),所有断言工厂都实现 RoutePredicateFactory
接口,并继承 AbstractRoutePredicateFactory<T>
。
以 HeaderRoutePredicateFactory
为例:
public class HeaderRoutePredicateFactory extends
AbstractRoutePredicateFactory<HeaderRoutePredicateFactory.Config> {
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
// 判断请求头是否匹配
}
};
}
@Validated
public static class Config {
// 存放配置参数,如 header 名、值等
}
}
关键点总结 ✅:
- 继承
AbstractRoutePredicateFactory<Config>
,泛型是内部配置类 - 实现
apply(Config)
方法,返回一个Predicate<ServerWebExchange>
- 定义静态内部类
Config
,用于接收外部配置参数(支持 JSR-303 校验)
⚠️ 注意:ServerWebExchange
是 WebFlux 中的核心上下文对象,包含请求、响应、会话等信息。
3. 实现自定义断言工厂
假设我们有这样一个需求:根据用户是否为“黄金会员”将请求路由到不同的后端服务器。
- 黄金会员 → 高性能服务器(https://fastserver)
- 普通用户 → 普通服务器(https://slowserver)
如何判断?通过 Cookie 中的 customerId
查询一个服务接口 GoldenCustomerService
获取用户等级。
基于此,我们创建自定义断言工厂:
public class GoldenCustomerRoutePredicateFactory extends
AbstractRoutePredicateFactory<GoldenCustomerRoutePredicateFactory.Config> {
private final GoldenCustomerService goldenCustomerService;
public GoldenCustomerRoutePredicateFactory(GoldenCustomerService goldenCustomerService) {
super(Config.class);
this.goldenCustomerService = goldenCustomerService;
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return (ServerWebExchange exchange) -> {
List<HttpCookie> cookies = exchange.getRequest()
.getCookies()
.getOrDefault(config.getCustomerIdCookie(), null);
boolean isGolden;
if (cookies == null || cookies.isEmpty()) {
isGolden = false;
} else {
String customerId = cookies.get(0).getValue();
isGolden = goldenCustomerService.isGoldenCustomer(customerId);
}
// 根据配置决定返回 true 还是 false
return config.isGolden() ? isGolden : !isGolden;
};
}
@Validated
public static class Config {
private boolean isGolden = true;
@NotEmpty
private String customerIdCookie = "customerId";
// getter/setter 省略
public boolean isGolden() { return isGolden; }
public void setGolden(boolean golden) { isGolden = golden; }
public String getCustomerIdCookie() { return customerIdCookie; }
public void setCustomerIdCookie(String customerIdCookie) { this.customerIdCookie = customerIdCookie; }
}
}
📌 要点说明:
- 构造函数中调用
super(Config.class)
是必须的,否则无法正确绑定配置 isGolden
字段决定了这个断言是用来匹配黄金用户还是普通用户- ✅ 一个工厂支持双向匹配,避免写两个类
- ❌ 注意线程安全:
goldenCustomerService
必须是无状态或线程安全的服务
4. 注册自定义断言工厂
Spring Cloud Gateway 会自动扫描所有类型为 RoutePredicateFactory
的 Bean。所以我们只需要把它注册成 Spring Bean 即可。
@Configuration
public class CustomPredicatesConfig {
@Bean
public GoldenCustomerRoutePredicateFactory goldenCustomer(
GoldenCustomerService goldenCustomerService) {
return new GoldenCustomerRoutePredicateFactory(goldenCustomerService);
}
}
假设 GoldenCustomerService
实现如下(测试用):
@Service
public class GoldenCustomerService {
public boolean isGoldenCustomer(String customerId) {
return "baeldung".equals(customerId); // 模拟黄金用户
}
}
⚠️ 踩坑提醒:如果自定义工厂没有生效,请检查:
- 是否正确添加了
@Bean
- 泛型
Config
是否被@Validated
修饰 - 类名是否以
RoutePredicateFactory
结尾(这是命名约定)
5. 使用自定义断言配置路由
注册完成后,就可以在路由中使用 GoldenCustomer
断言了。支持两种方式:Java API 和 YAML。
5.1 使用 Fluent API 配置
@Bean
public RouteLocator routes(RouteLocatorBuilder builder,
GoldenCustomerRoutePredicateFactory gf) {
return builder.routes()
.route("golden_route", r -> r.path("/api/**")
.uri("https://fastserver")
.predicate(gf.apply(new Config(true, "customerId"))))
.route("common_route", r -> r.path("/api/**")
.uri("https://slowserver")
.predicate(gf.apply(new Config(false, "customerId"))))
.build();
}
📌 说明:
- 第一个路由匹配黄金用户(
isGolden=true
) - 第二个路由匹配非黄金用户(
isGolden=false
) - 两个路由都拦截
/api/**
,优先级由定义顺序决定(前面的优先)
5.2 使用 YAML 配置
spring:
cloud:
gateway:
routes:
- id: golden_route
uri: https://fastserver
predicates:
- Path=/api/**
- GoldenCustomer=true
- id: common_route
uri: https://slowserver
predicates:
- Path=/api/**
- name: GoldenCustomer
args:
golden: false
customerIdCookie: customerId
📌 YAML 语法说明:
写法 | 适用场景 |
---|---|
GoldenCustomer=true |
参数简单,仅一个 boolean 或 String |
name: GoldenCustomer + args: |
参数复杂,需传多个字段或嵌套对象 |
✅ 断言名称 GoldenCustomer
是从 GoldenCustomerRoutePredicateFactory
自动推导而来(去掉后缀 + 驼峰转连字符)
⚠️ 若命名不规范会导致找不到断言
6. 测试验证
我们使用 httpbin.org 作为后端服务进行测试,它能回显所有请求头,非常适合验证路由和过滤器行为。
同时添加两个过滤器用于验证结果:
filters:
- AddRequestHeader=Goldencustomer, ${isGolden} # 动态添加 header
- StripPrefix=1 # 去掉 /api 前缀
场景一:普通用户(无 Cookie)
curl http://localhost:8080/api/headers
返回:
{
"headers": {
"Accept": "*/*",
"Goldencustomer": "false",
"Host": "httpbin.org",
"User-Agent": "curl/7.55.1"
}
}
场景二:黄金用户(带 Cookie)
curl -b customerId=baeldung http://localhost:8080/api/headers
返回:
{
"headers": {
"Accept": "*/*",
"Cookie": "customerId=baeldung",
"Goldencustomer": "true",
"Host": "httpbin.org",
"User-Agent": "curl/7.55.1"
}
}
✅ 结果符合预期:不同用户被正确路由,并携带了对应的 Goldencustomer
标识。
7. 总结
本文带你一步步实现了 Spring Cloud Gateway 的自定义断言工厂,适用于需要复杂业务逻辑参与路由决策的场景。
关键收获 ✅:
- 掌握
AbstractRoutePredicateFactory
的标准结构 - 理解
Config
类的作用与校验机制 - 学会 Java API 与 YAML 两种使用方式
- 避免常见注册失败的“坑”
⚠️ 生产建议:
- 尽量避免在断言中做耗时操作(如远程调用),可考虑缓存结果
- 对
Config
类做参数校验(@NotEmpty
,@Min
等) - 命名规范:
XxxRoutePredicateFactory
,否则无法自动识别
示例代码已托管至 GitHub:https://github.com/baeldung/spring-cloud-tutorial
分支:spring-cloud-gateway-custom-predicate