前言
前面一篇文章讲到了SoulPlugin和插件选择器,主要是讲了SoulPlugin的定义与实现,以及选择器的匹配,但是并没有把选择器的匹配给写完。那么本篇文章就去这里继续进行解析。和大家进一步进行深入的分析。先看下代码:
public class MatchStrategyUtils {
/**
* Match boolean.
*
* @param strategy the strategy
* @param conditionDataList the condition data list
* @param exchange the exchange
* @return the boolean
*/
public static boolean match(final Integer strategy, final List<ConditionData> conditionDataList, final ServerWebExchange exchange) {
String matchMode = MatchModeEnum.getMatchModeByCode(strategy);
MatchStrategy matchStrategy = ExtensionLoader.getExtensionLoader(MatchStrategy.class).getJoin(matchMode);
return matchStrategy.match(conditionDataList, exchange);
}
}
选择器匹配
- 获取匹配模式
从上面的代码中,我们可以看到首先是根据策略来获取匹配模式,那说到这里就要看看代码了,如下:
@RequiredArgsConstructor
@Getter
public enum MatchModeEnum {
/**
* And match mode enum.
*/
AND(0, "and"),
/**
* Or match mode enum.
*/
OR(1, "or");
private final int code;
private final String name;
/**
* get match mode name by code.
*
* @param code match mode code.
* @return match mode name.
*/
public static String getMatchModeByCode(final int code) {
return Arrays.stream(MatchModeEnum.values())
.filter(e -> e.code == code).findFirst()
.orElse(MatchModeEnum.AND)
.getName();
}
}
其实这就是一个枚举类,这里的匹配模式是根据or、and的策略来进行匹配。所以这块代码还是很简单的。那我们匹配到了自己的模式后,那就进入到下一步了。
- 获取ExtensionLoader
从最上面的代码中可以看到,先根据MatchStrategy.class来ExtensionLoader,然后根据获得到的ExtensionLoader来获取具体的匹配策略。说这么多,直接上代码:
ExtensionLoader.java
public static <T> ExtensionLoader<T> getExtensionLoader(final Class<T> clazz) {
if (clazz == null) {
throw new NullPointerException("extension clazz is null");
}
if (!clazz.isInterface()) {
throw new IllegalArgumentException("extension clazz (" + clazz + ") is not interface!");
}
if (!clazz.isAnnotationPresent(SPI.class)) {
throw new IllegalArgumentException("extension clazz (" + clazz + ") without @" + SPI.class + " Annotation");
}
ExtensionLoader<T> extensionLoader = (ExtensionLoader<T>) LOADERS.get(clazz);
if (extensionLoader != null) {
return extensionLoader;
}
LOADERS.putIfAbsent(clazz, new ExtensionLoader<>(clazz));
return (ExtensionLoader<T>) LOADERS.get(clazz);
}
这段代码很简单,根据传入的class来到缓存中获取ExtensionLoader,如果能获取到则直接返回,没有就直接new 一个,然后返回去。
- 正式匹配
@Join
public class AndMatchStrategy extends AbstractMatchStrategy implements MatchStrategy {
@Override
public Boolean match(final List<ConditionData> conditionDataList, final ServerWebExchange exchange) {
return conditionDataList
.stream()
.allMatch(condition -> OperatorJudgeFactory.judge(condition, buildRealData(condition, exchange)));
}
}
@Join
public class OrMatchStrategy extends AbstractMatchStrategy implements MatchStrategy {
@Override
public Boolean match(final List<ConditionData> conditionDataList, final ServerWebExchange exchange) {
return conditionDataList
.stream()
.anyMatch(condition -> OperatorJudgeFactory.judge(condition, buildRealData(condition, exchange)));
}
}
上面的代码就是两种策略的实现类,但是我们发现这两个都是继承了AndMatchStrategy,并且实现了MatchStrategy,我们还是要来看看MatchStrategy代码:
@SPI
public interface MatchStrategy {
/**
* this is condition match.
*
* @param conditionDataList condition list.
* @param exchange [email protected] ServerWebExchange}
* @return true is match , false is not match.
*/
Boolean match(List<ConditionData> conditionDataList, ServerWebExchange exchange);
}
MatchStrategy接口类中只是定义了一个方法,[email protected] 注解,这个先放一放,后面再关注。接着继续看看AbstractMatchStrategy抽象类的代码实现:
abstract class AbstractMatchStrategy {
/**
* Build real data string.
*
* @param condition the condition
* @param exchange the exchange
* @return the string
*/
String buildRealData(final ConditionData condition, final ServerWebExchange exchange) {
String realData = "";
ParamTypeEnum paramTypeEnum = ParamTypeEnum.getParamTypeEnumByName(condition.getParamType());
switch (paramTypeEnum) {
case HEADER:
final HttpHeaders headers = exchange.getRequest().getHeaders();
final List<String> list = headers.get(condition.getParamName());
if (CollectionUtils.isEmpty(list)) {
return realData;
}
realData = Objects.requireNonNull(headers.get(condition.getParamName())).stream().findFirst().orElse("");
break;
case URI:
realData = exchange.getRequest().getURI().getPath();
break;
case QUERY:
final MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
realData = queryParams.getFirst(condition.getParamName());
break;
case HOST:
realData = HostAddressUtils.acquireHost(exchange);
break;
case IP:
realData = HostAddressUtils.acquireIp(exchange);
break;
case POST:
final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
realData = (String) ReflectUtils.getFieldValue(soulContext, condition.getParamName());
break;
default:
break;
}
return realData;
}
}
在这个抽象类中,它首先会根据参数类型获取paramTypeEnum,然后根据paramTypeEnum来判断调用那个流程。这里有HEADER、URI等等。这里代码先不细说,我们回到两个策略类的juge方法,这个方法是OperatorJudgeFactory类中的,代码如下:
public class OperatorJudgeFactory {
private static final Map<String, OperatorJudge> OPERATOR_JUDGE_MAP = Maps.newHashMapWithExpectedSize(4);
static {
OPERATOR_JUDGE_MAP.put(OperatorEnum.EQ.getAlias(), new EqOperatorJudge());
OPERATOR_JUDGE_MAP.put(OperatorEnum.MATCH.getAlias(), new MatchOperatorJudge());
OPERATOR_JUDGE_MAP.put(OperatorEnum.LIKE.getAlias(), new LikeOperatorJudge());
OPERATOR_JUDGE_MAP.put(OperatorEnum.REGEX.getAlias(), new RegExOperatorJudge());
}
/**
* judge request realData has by pass.
* @param conditionData condition data
* @param realData realData
* @return is true pass is false not pass
*/
public static Boolean judge(final ConditionData conditionData, final String realData) {
if (Objects.isNull(conditionData) || StringUtils.isBlank(realData)) {
return false;
}
return OPERATOR_JUDGE_MAP.get(conditionData.getOperator()).judge(conditionData, realData);
}
}
从代码中可以看出类中在一个缓存,这个缓存存着几种操作类型,然后你如果去看OPERATOR_JUDGE_MAP.get(conditionData.getOperator()).judge(conditionData, realData)这行代码,会发现,这个juge方法也有匹配的几个类。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xFnMzir3-1611235899159)(https://uploader.shimo.im/f/Hq3s6RQKjeYs1IKr.png!thumbnail?fileGuid=hgKJCgVPG68kYqVq)]
在这里,我们以MatchOperatorJudge类为例:
public class MatchOperatorJudge implements OperatorJudge {
@Override
public Boolean judge(final ConditionData conditionData, final String realData) {
if (Objects.equals(ParamTypeEnum.URI.getName(), conditionData.getParamType())) {
return PathMatchUtils.match(conditionData.getParamValue().trim(), realData);
}
return realData.contains(conditionData.getParamValue().trim());
}
}
在这个类的juge方法中,有一这么一行代码:PathMatchUtils.match(conditionData.getParamValue().trim(), realData),这里是通过PathMatchUtils工具类的match来进行匹配的。代码如下:
public class PathMatchUtils {
private static final AntPathMatcher MATCHER = new AntPathMatcher();
/**
* Match boolean.
*
* @param matchUrls the ignore urls
* @param path the path
* @return the boolean
*/
public static boolean match(final String matchUrls, final String path) {
return Splitter.on(",").omitEmptyStrings().trimResults().splitToList(matchUrls).stream().anyMatch(url -> reg(url, path));
}
private static boolean reg(final String pattern, final String path) {
return MATCHER.match(pattern, path);
}
}
其实到这里,匹配的代码就差不多,那今天就到此结束了,后面准备从数据同步的角度来看看功能是如何实现的。
总结
这篇文章只是给上篇文章扫了个尾,主要从:
- 获取匹配模式
- 获取ExtensionLoader
- 正式匹配
这几个几个方面来进行解析的。后面将要从其他角度来继续进行代码分析。
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/1290.html