SpringCloudGateway的predicates配置实现原理

以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上的呢?

  1. 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]);
		}
	}
    .......
  }
  1. 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);
	}
  }
  1. 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;
	}
  }
}
  1. 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;
			}
		};
       .........
	}
  1. 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;
		});
	}
	.........
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值