以PathRoutePredicateFactory为例
PathRoutePredicateFactory类实现
/**
* @author Spencer Gibb
* @author Dhawal Kapil
*/
public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory<PathRoutePredicateFactory.Config> {
........
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("patterns", MATCH_TRAILING_SLASH);
}
@Override
public ShortcutType shortcutType() {
return ShortcutType.GATHER_LIST_TAIL_FLAG;
}
.....
public static class Config {
private List<String> patterns = new ArrayList<>();
private boolean matchTrailingSlash = true;
.......
}
}
PathRoutePredicateFactory$Config 配置包含了两个属性 patterns 和 matchTrailingSlash
配置文件
spring:
cloud:
gateway:
routes:
- id: test
uri: https://www.baidu.com
predicates:
- Path=/api/**,/api3/**,false
- Method=POST,GET,PUT
filters:
- StripPrefix=1
那么怎么让 /api/,/api3/ 配置设置到 patterns,false设置到matchTrailingSlash上的呢?
- PredicateDefinition 是对Predicate的定义
public class PredicateDefinition {
@NotNull
private String name;
private Map<String, String> args = new LinkedHashMap<>();
public PredicateDefinition() {
}
public PredicateDefinition(String text) {
int eqIdx = text.indexOf('=');
if (eqIdx <= 0) {
throw new ValidationException(
"Unable to parse PredicateDefinition text '" + text + "'" + ", must be of the form name=value");
}
setName(text.substring(0, eqIdx));
//此处是拆分 配置文件的字符串 这里打断点
String[] args = tokenizeToStringArray(text.substring(eqIdx + 1), ",");
for (int i = 0; i < args.length; i++) {
// 生成的名字 _genkey_0, _genkey_1
this.args.put(NameUtils.generateName(i), args[i]);
}
}
.......
}
- RouteDefinitionRouteLocator 这个路由定义查找
public class RouteDefinitionRouteLocator implements RouteLocator {
........
@SuppressWarnings("unchecked")
private AsyncPredicate<ServerWebExchange> lookup(RouteDefinition route, PredicateDefinition predicate) {
RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());
if (factory == null) {
throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName());
}
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition " + route.getId() + " applying " + predicate.getArgs() + " to "
+ predicate.getName());
}
// @formatter:off
// 这里对 predicate 属性的绑定,重点是 configurationService 打断点
Object config = this.configurationService.with(factory)
.name(predicate.getName())
.properties(predicate.getArgs())
.eventFunction((bound, properties) -> new PredicateArgsEvent(
RouteDefinitionRouteLocator.this, route.getId(), properties))
.bind();
// @formatter:on
return factory.applyAsync(config);
}
}
- ConfigurationService主要实现绑定
public class ConfigurationService implements ApplicationEventPublisherAware {
.......
/* for testing */ static <T> T bindOrCreate(Bindable<T> bindable, Map<String, Object> properties,
String configurationPropertyName, Validator validator, ConversionService conversionService) {
// see ConfigurationPropertiesBinder from spring boot for this definition.
BindHandler handler = new IgnoreTopLevelConverterNotFoundBindHandler();
if (validator != null) { // TODO: list of validators?
handler = new ValidationBindHandler(handler, validator);
}
List<ConfigurationPropertySource> propertySources = Collections
.singletonList(new MapConfigurationPropertySource(properties));
//创建 Binder
return new Binder(propertySources, null, conversionService).bindOrCreate(configurationPropertyName, bindable,
handler);
}
.......
public static class ConfigurableBuilder<T, C extends Configurable<T> & ShortcutConfigurable>
extends AbstractBuilder<T, ConfigurableBuilder<T, C>> {
........
@Override
protected T doBind() {
Bindable<T> bindable = Bindable.of(this.configurable.getConfigClass());
T bound = bindOrCreate(bindable, this.normalizedProperties, this.configurable.shortcutFieldPrefix(),
/* this.name, */this.service.validator.get(), this.service.conversionService.get());
return bound;
}
}
public static abstract class AbstractBuilder<T, B extends AbstractBuilder<T, B>> {
.....
@Override
protected Map<String, Object> normalizeProperties() {
if (this.service.beanFactory != null) {
//这里可以 PathRoutePredicateFactory 的定义的 shortcutType => ShortcutType.GATHER_LIST_TAIL_FLAG
return this.configurable.shortcutType().normalize(this.properties, this.configurable,
this.service.parser, this.service.beanFactory);
}
return super.normalizeProperties();
}
public T bind() {
validate();
Assert.hasText(this.name, "name may not be empty");
Assert.isTrue(this.properties != null || this.normalizedProperties != null,
"properties and normalizedProperties both may not be null");
//重点看一下 normalizeProperties的实现 这里打断点
if (this.normalizedProperties == null) {
this.normalizedProperties = normalizeProperties();
}
//实现真正的绑定
T bound = doBind();
if (this.eventFunction != null && this.service.publisher != null) {
ApplicationEvent applicationEvent = this.eventFunction.apply(bound, this.normalizedProperties);
this.service.publisher.publishEvent(applicationEvent);
}
return bound;
}
}
}
- ShortcutType.GATHER_LIST_TAIL_FLAG的实现
/**
* List is all elements except last which is a boolean flag.
*/
GATHER_LIST_TAIL_FLAG {
@Override
public Map<String, Object> normalize(Map<String, String> args, ShortcutConfigurable shortcutConf,
SpelExpressionParser parser, BeanFactory beanFactory) {
Map<String, Object> map = new HashMap<>();
// field order should be of size 1
List<String> fieldOrder = shortcutConf.shortcutFieldOrder();
Assert.isTrue(fieldOrder != null && fieldOrder.size() == 2,
"Shortcut Configuration Type GATHER_LIST_HEAD must have shortcutFieldOrder of size 2");
List<String> values = new ArrayList<>(args.values());
if (!values.isEmpty()) {
// strip boolean flag if last entry is true or false
int lastIdx = values.size() - 1;
String lastValue = values.get(lastIdx);
if ("true".equalsIgnoreCase(lastValue) || "false".equalsIgnoreCase(lastValue)
|| lastValue == null) {
values = values.subList(0, lastIdx);
map.put(fieldOrder.get(1), getValue(parser, beanFactory, lastValue));
}
}
String fieldName = fieldOrder.get(0);
map.put(fieldName, values.stream().map(value -> getValue(parser, beanFactory, value))
.collect(Collectors.toList()));
return map;
}
};
.........
}
- Binder实现真正的绑定
public class Binder {
......
private Object bindDataObject(ConfigurationPropertyName name, Bindable<?> target, BindHandler handler,
Context context, boolean allowRecursiveBinding) {
if (isUnbindableBean(name, target, context)) {
return null;
}
Class<?> type = target.getType().resolve(Object.class);
BindMethod bindMethod = target.getBindMethod();
if (!allowRecursiveBinding && context.isBindingDataObject(type)) {
return null;
}
DataObjectPropertyBinder propertyBinder = (propertyName, propertyTarget) -> bind(name.append(propertyName),
propertyTarget, handler, context, false, false);
return context.withDataObject(type, () -> {
for (DataObjectBinder dataObjectBinder : this.dataObjectBinders.get(bindMethod)) {
Object instance = dataObjectBinder.bind(name, target, context, propertyBinder);
if (instance != null) {
return instance;
}
}
return null;
});
}
.........
}