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. 实现自定义断言工厂

假设我们有这样一个需求:根据用户是否为“黄金会员”将请求路由到不同的后端服务器。

如何判断?通过 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


原始标题:Spring Cloud Gateway Routing Predicate Factories | Baeldung