Написание собственного Predicate — RoutePredicateFactory
Когда встроенных предикатов недостаточно по логике или производительности, пишут собственные RoutePredicateFactory. Ниже — полный пример: реализуем предикат, который проверяет наличие параметра X-Feature и сопоставляет его по списку допустимых значений, при этом конфигурируемый через YAML.
1) Структура — класс предиката
Ключевые моменты:
Наследуемся от AbstractRoutePredicateFactory<Config>. Это даёт поддержку автоконфигурируемой десериализации конфигурации из YAML в Config.
apply(Config) возвращает Predicate<ServerWebExchange>, который будет выполняться для каждого запроса.
Config — POJO с геттерами/сеттерами: Spring автоматически маппит значения из YAML (через Binder).
2) Регистрация (component или bean)
Если не использовать @Component, можно зарегистрировать как bean через конфигурацию.
3) Использование в YAML
В AbstractRoutePredicateFactory стандартная разбивка аргументов (если конфиг простой) позволит подставить список значений. Для более сложной настройки (ключ:значение) необходимо переопределить shortcutFieldOrder() или использовать вложенный Config с именованными полями.
4) Использование в Java DSL
Если хотите inline-предикат в коде (без фабрики), Java DSL позволяет:
Однако такой inline-предикат теряет преимущества конфигурируемости через YAML и переиспользуемости.
#Java #middle #Spring_Cloud_Gateway
Когда встроенных предикатов недостаточно по логике или производительности, пишут собственные RoutePredicateFactory. Ниже — полный пример: реализуем предикат, который проверяет наличие параметра X-Feature и сопоставляет его по списку допустимых значений, при этом конфигурируемый через YAML.
1) Структура — класс предиката
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.web.server.ServerWebExchange;
import java.util.function.Predicate;
import java.util.List;
public class XFeatureRoutePredicateFactory extends AbstractRoutePredicateFactory<XFeatureRoutePredicateFactory.Config> {
public XFeatureRoutePredicateFactory() {
super(Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
List<String> allowed = config.getAllowedValues();
boolean ignoreCase = config.isIgnoreCase();
return exchange -> {
List<String> headerValues = exchange.getRequest().getHeaders().get("X-Feature");
if (headerValues == null || headerValues.isEmpty()) {
return false;
}
for (String hv : headerValues) {
for (String allowedVal : allowed) {
if (ignoreCase) {
if (hv.equalsIgnoreCase(allowedVal)) return true;
} else {
if (hv.equals(allowedVal)) return true;
}
}
}
return false;
};
}
public static class Config {
private List<String> allowedValues;
private boolean ignoreCase = true;
public List<String> getAllowedValues() { return allowedValues; }
public void setAllowedValues(List<String> allowedValues) { this.allowedValues = allowedValues; }
public boolean isIgnoreCase() { return ignoreCase; }
public void setIgnoreCase(boolean ignoreCase) { this.ignoreCase = ignoreCase; }
}
}
Ключевые моменты:
Наследуемся от AbstractRoutePredicateFactory<Config>. Это даёт поддержку автоконфигурируемой десериализации конфигурации из YAML в Config.
apply(Config) возвращает Predicate<ServerWebExchange>, который будет выполняться для каждого запроса.
Config — POJO с геттерами/сеттерами: Spring автоматически маппит значения из YAML (через Binder).
2) Регистрация (component или bean)
import org.springframework.stereotype.Component;
@Component
public class XFeatureRoutePredicateFactory extends AbstractRoutePredicateFactory<XFeatureRoutePredicateFactory.Config> {
// ... код как выше
}
Если не использовать @Component, можно зарегистрировать как bean через конфигурацию.
3) Использование в YAML
spring:
cloud:
gateway:
routes:
- id: feature-route
uri: http://feature-service
predicates:
- XFeature=allowed1,allowed2
В AbstractRoutePredicateFactory стандартная разбивка аргументов (если конфиг простой) позволит подставить список значений. Для более сложной настройки (ключ:значение) необходимо переопределить shortcutFieldOrder() или использовать вложенный Config с именованными полями.
4) Использование в Java DSL
Если хотите inline-предикат в коде (без фабрики), Java DSL позволяет:
.route("xfeature-inline", r -> r.p(exchange -> {
List<String> values = exchange.getRequest().getHeaders().get("X-Feature");
return values != null && values.stream().anyMatch(v -> v.equalsIgnoreCase("allowed1"));
}).uri("http://feature-service"))Однако такой inline-предикат теряет преимущества конфигурируемости через YAML и переиспользуемости.
#Java #middle #Spring_Cloud_Gateway
👍2
Работа с конфигурационными объектами и AbstractRoutePredicateFactory — тонкости
Shortcut configuration vs. full Config binding
Если RoutePredicateFactory использует короткий синтаксис (например: MyPred=val1,val2), то AbstractRoutePredicateFactory поддерживает маппинг по shortcutFieldOrder() — список полей Config, которые будут заполнены по порядку.
Для явной структуры лучше использовать именованные свойства в YAML и обычный биндинг в Config.
Валидация конфигурации
Валидируйте конфиг в конструкторе предиката или в apply() — например, проверить, что список не пуст. Ошибки конфигурации лучше бросать на старте приложения, а не при первом запросе.
Сериализация/десериализация сложных типов
Для сложных типов (например, Duration, Pattern, InetAddress[]) используйте соответствующие конвертеры или храните строковые представления и парсите в Config.
Потокобезопасность
Predicate возвращаемый apply() должен быть потокобезопасным и не содержать mutable state, зависящего от запроса; храните precomputed структуры (например Pattern), а не парсьте каждый раз.
Логирование и мониторинг
Для сложных предикатов логируйте причины отказа (на низком уровне) либо метрики отказов, чтобы упростить отладку некорректного маршрутизации.
Примеры: несколько реальных сценариев и лучшие практики
Пример 1 — комбинация Host + Path + Method (YAML)
Пример 2 — OR-логика через два маршрута (YAML)
Пример 3 — кастомный предикат с конфигом в YAML
#Java #middle #Spring_Cloud_Gateway
Shortcut configuration vs. full Config binding
Если RoutePredicateFactory использует короткий синтаксис (например: MyPred=val1,val2), то AbstractRoutePredicateFactory поддерживает маппинг по shortcutFieldOrder() — список полей Config, которые будут заполнены по порядку.
Для явной структуры лучше использовать именованные свойства в YAML и обычный биндинг в Config.
Валидация конфигурации
Валидируйте конфиг в конструкторе предиката или в apply() — например, проверить, что список не пуст. Ошибки конфигурации лучше бросать на старте приложения, а не при первом запросе.
Сериализация/десериализация сложных типов
Для сложных типов (например, Duration, Pattern, InetAddress[]) используйте соответствующие конвертеры или храните строковые представления и парсите в Config.
Потокобезопасность
Predicate возвращаемый apply() должен быть потокобезопасным и не содержать mutable state, зависящего от запроса; храните precomputed структуры (например Pattern), а не парсьте каждый раз.
Логирование и мониторинг
Для сложных предикатов логируйте причины отказа (на низком уровне) либо метрики отказов, чтобы упростить отладку некорректного маршрутизации.
Примеры: несколько реальных сценариев и лучшие практики
Пример 1 — комбинация Host + Path + Method (YAML)
routes:
- id: users-route
uri: lb://user-service
predicates:
- Host=api.example.com
- Path=/users/**
- Method=GET
Пояснение: типичный маршрут, дешёвые проверки (Host/Method/Path) — быстрый short-circuit.
Пример 2 — OR-логика через два маршрута (YAML)
routes:
- id: mobile-route
uri: lb://mobile-backend
predicates:
- Header=X-Client, ^mobile-.*
- Path=/api/**
- id: fallback-route
uri: lb://web-backend
predicates:
- Path=/api/**
Пояснение: первый маршрут отведёт мобильный трафик на mobile-backend; второй поймает остальные запросы по тому же Path — это простой способ построить OR-поведение без кастомных предикатов.
Пример 3 — кастомный предикат с конфигом в YAML
predicates:
- XFeature=allowedA,allowedB
(см. реализацию XFeatureRoutePredicateFactory выше).
#Java #middle #Spring_Cloud_Gateway
👍2