整体方案
【平台化引擎-1】根据maven坐标—获取到jar的Class文件(URLClassLoader)
【平台化引擎-2】— 参数扁平与结构化操作
在这两篇文章中,我们解决了硬编码后注册maven坐标,可以被JVM运行时感知,并执行相应逻辑。而参数在页面上扁平化展示+配置。JVM可以在运行的时候获取到对应的参数值。
而本篇文章重点描述下如何实现平台化的能力。

运行加载流程:

实现
2.1 引入QlExpress脚本语言
引入依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>QLExpress</artifactId>
<version>3.2.0</version>
</dependency>
和java语法相比,要避免的一些ql写法错误
不支持try{}catch{}
注释目前只支持 /** **/,不支持单行注释 //
不支持java8的lambda表达式
不支持for循环集合操作for (Item item : list)
弱类型语言,请不要定义类型声明,更不要用Template(Map<String, List>之类的)
array的声明不一样
min,max,round,print,println,like,in 都是系统默认函数的关键字,请不要作为变量名
实现demo:用户自定义了函数方法。会传入到表达式引擎中,以便运行自定义方法。
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import org.apache.commons.collections4.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class TestExpress {
//调试组件
public static void main(String[] args) throws Exception {
//方法名
String methodName = "stringEquals";
//传入的参数
List<String> methodArgs = Lists.newArrayList("str1", "str2");
//传入的脚本语言
String methodCode = "if(str1==null && str2==null){\n return true;\n}\nif(str1==null || str2==null) {\n return false;\n}\nif(str1.equals(str2)) {\n return true;\n}\nreturn false;";
//待执行表达式(自定义)
String userExpress = "stringEquals(a,b)";
//获取方法表达式
String functionExpress = getFunctionExpress(methodName, methodCode, methodArgs);
//获取表达式
StringBuilder express = new StringBuilder();
express.append(functionExpress);
express.append(userExpress);
//上下文
Map<String, Object> map = Maps.newHashMap();
map.put("a", 1);
map.put("b", 1);
DefaultContext<String, Object> defaultContext = new DefaultContext<>();
defaultContext.putAll(map);
ExpressRunner runner = new ExpressRunner();
//打印表达式
System.out.println(express.toString());
//执行结构
System.out.println(runner.execute(express.toString(), defaultContext, null, true, false));
}
private static String getFunctionExpress(String name, String udfCode, List<String> udfCodeArgs) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("function ");
stringBuilder.append(name + " ( ");
if (CollectionUtils.isNotEmpty(udfCodeArgs)) {
String args = udfCodeArgs.stream().map(arg -> "Object " + arg).collect(Collectors.joining(","));
stringBuilder.append(args);
}
stringBuilder.append(" ){ ");
stringBuilder.append(udfCode);
stringBuilder.append(" }; ");
return stringBuilder.toString();
}
}
执行结果:
function stringEquals ( Object str1,Object str2 ){ if(str1==null && str2==null){
return true;
}
if(str1==null || str2==null) {
return false;
}
if(str1.equals(str2)) {
return true;
}
return false; }; stringEquals(a,b)
true
2.2 通过jar执行函数
(1)定义UDF函数
用户可以自定义实现UDF函数。
public abstract class UdfFunction {
protected ClassLoader classLoader;
public UdfFunction(ClassLoader classLoader) {
this.classLoader = classLoader;
}
}
public class ArrayLengthUdfFunction extends UdfFunction {
public ArrayLengthUdfFunction() {
super(ClassLoader.getSystemClassLoader());
}
public ArrayLengthUdfFunction(ClassLoader classLoader) {
super(classLoader);
}
public static Integer eval(Object input) {
if (input == null) {
return 0;
}
if (input instanceof Collection) {
return ((Collection<?>) input).size();
}
if (input instanceof CharSequence) {
return ((CharSequence) input).length();
}
if (input.getClass().isArray()) {
return ((Object[]) input).length;
}
if (input instanceof Map) {
return ((Map<?, ?>) input).size();
}
return 1;
}
}
(2)加载函数的工具类
import com.google.common.collect.Maps;
import com.tellme.platform.exception.FrameworkException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
public class URLClassLoaderUtil {
//将加载的类加载器缓存起来
private static final Map<String, URLClassLoader> loaderMap = Maps.newConcurrentMap();
public static Class<?> getUDFClass(String jarUrl, String className) throws Exception {
URLClassLoader classLoader = getClassLoader(jarUrl);
return Class.forName(className, true, classLoader);
}
/**
* 判断方法是否存在
*/
public static Method getMethod(Class<?> clazz, String functionName) {
Method[] methods = clazz.getMethods();
Optional<Method> methodOptional =
Arrays.stream(methods).filter(me -> me.getName().equals(functionName)).findFirst();
if (methodOptional.isEmpty()) {
throw new FrameworkException("函数:" + functionName + " 不存在");
}
return methodOptional.get();
}
public static Object execute(Class<?> clazz, String methodName, Object... args) {
try {
Class<?>[] argClass = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
Method method = clazz.getMethod(methodName, argClass);
return method.invoke(clazz.newInstance(), args);
} catch (Exception e) {
throw new FrameworkException("函数执行错误", e);
}
}
public static <T> T getInstanceWithClassloader(Class<T> clazz) throws Exception {
try {
Constructor<T> constructor = clazz.getConstructor(ClassLoader.class);
return constructor.newInstance(clazz.getClassLoader());
} catch (Exception e) {
return clazz.newInstance();
}
}
private static synchronized URLClassLoader getClassLoader(String jarUrl) {
try {
URL[] urls = new URL[]{new URL(jarUrl)};
return loaderMap.computeIfAbsent(jarUrl, jar -> new URLClassLoader(urls));
} catch (Exception e) {
throw new FrameworkException("类库加载失败", e);
}
}
}
2.3 代码实现
(1)获取到平台上的配置
两类配置:
- lib:jar私服地址+类全名;
- code:参数列表、脚本函数、方法名;
public interface IUdfForQuery {
UDFConfig getFunctionConfig(String functionName);
}
public class UdfForQueryForMock implements IUdfForQuery {
private static final Integer EXPIRE_SECONDS = 3600;
private static final Integer MAXIMUM_SIZE = 10000;
private static final UdfForQueryForMock INSTANCE = new UdfForQueryForMock();
private static final LoadingCache<String, UDFConfig> UDF_CACHE =
CacheBuilder.newBuilder()
.concurrencyLevel(5)
.expireAfterWrite(Duration.ofSeconds(EXPIRE_SECONDS))
.maximumSize(MAXIMUM_SIZE)
.build(new CacheLoader<String, UDFConfig>() {
@Override
public UDFConfig load(String name) throws Exception {
//此处获取配置,mock实现,可rpc调用页面配置
UDFConfig result = new UDFConfig();
//脚本配置
if ("stringEquals".equals(name)) {
result.setUdfType(2);
result.setName("stringEquals");
result.setUdfCodeArgs(Lists.newArrayList("str1", "str2"));
result.setUdfCode("if(str1==null && str2==null){\n return true;\n}\nif(str1==null || str2==null) {\n return false;\n}\nif(str1.equals(str2)) {\n return true;\n}\nreturn false;");
} else if ("convertToList".equals(name)) {
result.setUdfType(1);
result.setName("convertToList");
result.setClassName("com.tellme.ArrayLengthUdfFunction");
result.setJarUrl("http://私服地址/xxx.jar");
} else if ("len".equals(name)) {
result.setUdfType(1);
result.setName("len");
result.setClassName("com.tellme.ConvertToList");
result.setJarUrl("http://私服地址/xxx.jar");
} else {
throw new FrameworkException("未找到配置");
}
return result;
}
});
private UdfForQueryForMock() {
}
public static UdfForQueryForMock getInstance() {
return INSTANCE;
}
@Override
public UDFConfig getFunctionConfig(String functionName) {
try {
return UDF_CACHE.get(functionName);
} catch (Exception e) {
throw new FrameworkException("函数:" + functionName + "获取配置失败,", e);
}
}
}
model对象:
public class UDFConfig {
/**
* udf的配置
*/
private String name;
/**
* udf的类型
*/
private Integer udfType;
/**
* jar的class类名
*/
private String className;
/**
* jar的url地址
*/
private String jarUrl;
/**
* 在线编辑的参数列表
* eg:"str1", "str2"
*/
private List<String> udfCodeArgs;
/**
* 脚本语言
*/
private String udfCode;
}
枚举:
public enum UDFTypeEnum {
LIB(1, "代码仓库"),
CODE(2, "在线编辑"),
INNNER(3, "内置函数");
}
(2)表达式组装处理器
convertExpress
方法是核心方法:
- 完成表达式字符替换(
.
替换为_
); - 完成参数的获取(结构化参数替换为扁平化参数)
- 完成脚本函数的构建;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.tellme.platform.exception.FrameworkException;
import com.tellme.platform.express.UDFConfig;
import com.tellme.platform.express.UDFTypeEnum;
import com.tellme.platform.express.rpc.IUdfForQuery;
import com.tellme.platform.express.rpc.UdfForQueryForRpc;
import com.tellme.platform.paramhandler.FlowData;
import lombok.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
public class ExpressBuildHandler {
private static final Set<String> DEFAULT_KEYWORD = Sets.newHashSet("null", "true", "false");
public static final int EXPRESS_PARAM = 1;
public static final int EXPRESS_METHOD = 2;
public static ExpressInfo convertExpress(String express, FlowData dataInfo) throws Exception {
ExpressInfo expressInfo = new ExpressInfo();
//根据字符位置,解析总的表达式,获取到方法名、参数名
List<ExpressVar> expressVarList = getVarFromExpress(express);
//重写表达式(替换表达式中的.为_)
expressInfo.setExpress(replaceSeparatorForExpress(express, expressVarList));
//构建表达式入参(将需要的入参,从流参数中解析出来)
expressInfo.setParamMap(getParamMap(expressVarList, dataInfo));
//构建脚本函数
expressInfo.setFunctionMap(getFunctionMap(expressVarList));
return expressInfo;
}
public static Map<String, ExpressFunction> getFunctionMap(List<ExpressVar> expressVarList) throws Exception {
Map<String, ExpressFunction> functionMap = Maps.newHashMap();
//处理表达式
for (ExpressVar entry : expressVarList) {
if (entry.getType() == EXPRESS_METHOD) {
String functionName = entry.getContext();
ExpressFunction expressFunction = getExpressFunction(functionName);
if (Objects.nonNull(expressFunction)) {
functionMap.put(functionName, expressFunction);
}
}
}
return functionMap;
}
public static Map<String, Object> getParamMap(List<ExpressVar> expressVarList, FlowData dataInfo) {
Map<String, Object> paramMap = Maps.newHashMap();
for (ExpressVar entry : expressVarList) {
if (entry.getType() == EXPRESS_PARAM) {
String valKey = replaceSeparatorForKey(entry.getContext());
if (!DEFAULT_KEYWORD.contains(valKey)) {
paramMap.put(valKey, getExpressReplaceValue(entry.getContext(), dataInfo));
}
}
}
return paramMap;
}
/**
* 重新表达式,替换分隔符
*/
private static String replaceSeparatorForExpress(String express, List<ExpressVar> expressVarList) {
int appendStart = 0;
StringBuilder resultBuilder = new StringBuilder();
//处理表达式
for (ExpressVar entry : expressVarList) {
//替换
if (entry.getType() == EXPRESS_PARAM) {
String valKey = replaceSeparatorForKey(entry.getContext());
Integer start = entry.getStart();
resultBuilder.append(express, appendStart, start);
resultBuilder.append(valKey);
appendStart = entry.getEnd();
}
}
if (appendStart < express.length()) {
resultBuilder.append(express, appendStart, express.length());
}
return resultBuilder.toString();
}
private static String replaceSeparatorForKey(String paramFieldName) {
return paramFieldName.replace(".", "_");
}
private static ExpressFunction getExpressFunction(String functionName) throws Exception {
IUdfForQuery udfForQuery = UdfForQueryForRpc.getInstance();
//根据方法名,获取到方法配置
UDFConfig udfConfig = udfForQuery.getFunctionConfig(functionName);
UDFTypeEnum udfTypeEnum = UDFTypeEnum.of(udfConfig.getUdfType());
switch (udfTypeEnum) {
case LIB:
return getExpressFunctionForLib(functionName, udfConfig);
case CODE:
return getExpressFunctionForCode(functionName, udfConfig);
case INNNER:
return getExpressFunctionForInner(functionName);
default:
return null;
}
}
//内置函数,什么也不做。
public static ExpressFunction getExpressFunctionForInner(String functionName) {
return ExpressFunction.builder().function(functionName).build();
}
private static Object getExpressReplaceValue(String field, FlowData dataInfo) {
String stringField = field.trim();
if (DEFAULT_KEYWORD.contains(stringField)) {
return stringField;
}
Object result = dataInfo.getData(stringField);
if (Objects.isNull(result)) {
return null;
}
return result;
}
private static List<ExpressVar> getVarFromExpress(String express) {
List<ExpressVar> result = Lists.newArrayList();
if (StringUtils.isBlank(express)) {
return result;
}
boolean strScope = false;
boolean varScope = false;
int varStart = 0;
int varEnd = 0;
char currentChar;
char preChar = 0;
char[] charList = express.toCharArray();
for (int i = 0; i < charList.length; i++) {
currentChar = charList[i];
if (i - 1 >= 0) {
preChar = charList[i - 1];
}
//遇到引号变换字符串scope
if (currentChar == '"' && preChar != '\\') {
strScope = !strScope;
}
//非字符串且非变量scope,遇到字母进入变量scope
if (!strScope && !varScope && Character.isLetter(currentChar)) {
varScope = true;
varStart = i;
varEnd = i + 1;
continue;
}
//当前处于字符串scope,进行累加或结束
if (varScope) {
if (Character.isLetterOrDigit(currentChar) || currentChar == '_' || currentChar == '.') {
varEnd++;
continue;
}
varScope = false;
if (currentChar == '(') {
result.add(new ExpressVar(varStart, varEnd, 2, express.substring(varStart, varEnd)));
} else {
result.add(new ExpressVar(varStart, varEnd, 1, express.substring(varStart, varEnd)));
}
}
}
if (strScope) {
throw new FrameworkException("字符串不完整");
}
if (varScope) {
result.add(new ExpressVar(varStart, varEnd, 1, express.substring(varStart, varEnd)));
}
return result;
}
/**
* 表达式方法解析器 reflection
*/
public static ExpressFunction getExpressFunctionForLib(String functionName, UDFConfig udfConfig) throws Exception {
Class<?> clazz;
try {
clazz = URLClassLoaderUtil.getUDFClass(udfConfig.getJarUrl(), udfConfig.getClassName());
} catch (Exception e) {
throw new FrameworkException(String.format("组件:%s加载失败", udfConfig.getName()), e);
}
Method method = URLClassLoaderUtil.getMethod(clazz, "eval");
Object instance = URLClassLoaderUtil.getInstanceWithClassloader(clazz);
//返回类配置
return ExpressFunction.builder()
.function(functionName)
.udfTypeEnum(UDFTypeEnum.of(udfConfig.getUdfType()))
.classInstance(instance)
.methodName(method.getName())
.paramTypes(method.getParameterTypes())
.build();
}
public static ExpressFunction getExpressFunctionForCode(String functionName, UDFConfig udfConfig) {
String udfCode = udfConfig.getUdfCode();
List<String> udfCodeArgs = udfConfig.getUdfCodeArgs();
return ExpressFunction.builder()
.function(functionName)
.udfTypeEnum(UDFTypeEnum.of(udfConfig.getUdfType()))
.udfFunctionImpl(getFunctionExpress(functionName, udfCode, udfCodeArgs))
.build();
}
private static String getFunctionExpress(String name, String udfCode, List<String> udfCodeArgs) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("function ");
stringBuilder.append(name + " ( ");
if (CollectionUtils.isNotEmpty(udfCodeArgs)) {
String args = udfCodeArgs.stream().map(arg -> "Object " + arg).collect(Collectors.joining(","));
stringBuilder.append(args);
}
stringBuilder.append(" ){ ");
stringBuilder.append(udfCode);
stringBuilder.append(" }; ");
return stringBuilder.toString();
}
@Getter
@Setter
@AllArgsConstructor
public static class ExpressVar {
private Integer start;
private Integer end;
private Integer type;
private String context;
}
@Getter
@Builder
@ToString
public static class ExpressFunction {
private String function;
private UDFTypeEnum udfTypeEnum;
private Object classInstance;
private String methodName;
private Class<?>[] paramTypes;
private String udfFunctionImpl;
}
@Getter
@Setter
public static class ExpressInfo {
private String express;
private Map<String, Object> paramMap;
private Map<String, ExpressFunction> functionMap;
}
}
(3)表达式执行处理器
import com.google.common.collect.Maps;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.tellme.platform.express.UDFTypeEnum;
import org.apache.commons.collections4.MapUtils;
import java.util.Map;
import java.util.Objects;
public class ExpressExecuteHandler {
private static final ExpressExecuteHandler INSTANCE = new ExpressExecuteHandler();
private final ExpressRunner runner;
private final Map<String, ExpressBuildHandler.ExpressFunction> expressFunctionMap;
private ExpressExecuteHandler() {
this.runner = new ExpressRunner();
this.expressFunctionMap = Maps.newConcurrentMap();
//填充基本函数
try {
runner.addFunctionOfServiceMethod("toJSON", new InnerQlExpress(), "toJSON", new Class<?>[]{Object.class}, null);
runner.addFunctionOfServiceMethod("fromJson", new InnerQlExpress(), "fromJson", new Class<?>[]{String.class}, null);
runner.addFunctionOfServiceMethod("isNotEmpty", new InnerQlExpress(), "isNotEmpty", new Class<?>[]{String.class}, null);
runner.addFunctionOfServiceMethod("isNotZero", new InnerQlExpress(), "isNotZero", new Class<?>[]{String.class}, null);
} catch (Exception e) {
throw new FrameworkException("初始化express函数", e);
}
}
public static ExpressExecuteHandler getInstance() {
return INSTANCE;
}
/**
* 将外部jar的方法注册到ExpressRunner中,以便在表达式中使用
*/
public synchronized void setFunction(ExpressBuildHandler.ExpressFunction expressFunction) throws Exception {
String functionName = expressFunction.getFunction();
//加载服务中的Function方法(使用Map防止多次注册)
if (Objects.isNull(expressFunctionMap.get(functionName))) {
expressFunctionMap.put(functionName, expressFunction);
runner.addFunctionOfServiceMethod(expressFunction.getFunction(),
expressFunction.getClassInstance(),
expressFunction.getMethodName(),
expressFunction.getParamTypes(), null);
}
}
/**
* 执行脚本语言
*
* @param express 脚本表达式
* @param defaultContext 环境变量(参数)
*/
private Object execute(String express, DefaultContext<String, Object> defaultContext) throws Exception {
return runner.execute(express, defaultContext, null, true, false);
}
/**
* 执行函数
*/
public Object calculateForResult(ExpressBuildHandler.ExpressInfo expressInfo) throws Exception {
StringBuilder finalExpress = new StringBuilder();
if (MapUtils.isNotEmpty(expressInfo.getFunctionMap())) {
for (ExpressBuildHandler.ExpressFunction expressFunction : expressInfo.getFunctionMap().values()) {
UDFTypeEnum udfTypeEnum = expressFunction.getUdfTypeEnum();
if (UDFTypeEnum.LIB.equals(udfTypeEnum)) {
this.setFunction(expressFunction);
}
if (UDFTypeEnum.CODE.equals(udfTypeEnum)) {
finalExpress.append(expressFunction.getUdfFunctionImpl());
}
}
}
finalExpress.append(expressInfo.getExpress());
DefaultContext<String, Object> defaultContext = new DefaultContext<>();
defaultContext.putAll(expressInfo.getParamMap());
return this.execute(finalExpress.toString(), defaultContext);
}
}
内置函数:
public class InnerQlExpress {
public boolean isNotEmpty(String value) {
return StringUtils.isNotBlank(value);
}
public boolean isNotZero(String value) {
return StringUtils.isNotEmpty(value) && Long.parseLong(value) != 0;
}
public Map<String, Object> fromJson(String val) {
return ObjectMapperUtils.fromJson(val);
}
public String toJSON(Object obj) {
return ObjectMapperUtils.toJSON(obj);
}
}
2.4 上下文流转对象
主要是处理扁平化对象。ParamHelper
工具类详见【平台化引擎-2】— 参数扁平与结构化操作
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.tellme.platform.maven.helper.ParamHelper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
/**
* 数据流对象。
* 1. 取参扁平化;
* 2. 存参结构化;
*/
public class FlowData implements Serializable {
private static final long serialVersionUID = -8677073254096521327L;
private String dataId;
private final Map<String, Object> data = Maps.newHashMap();
public FlowData() {
this.dataId = null;
}
public FlowData(String dataId) {
this.dataId = dataId;
}
public FlowData(FlowData flowData) {
this.dataId = flowData.dataId;
this.data.putAll(flowData.data);
}
public FlowData(String dataId, FlowData flowData) {
this.dataId = dataId;
this.data.putAll(flowData.data);
}
public String getDataId() {
return dataId;
}
public void setDataId(String dataId) {
this.dataId = dataId;
}
/**
* 按照路径找到相应值
*/
public Object getData(String fieldPath) {
if (StringUtils.isEmpty(fieldPath)) {
return null;
}
String[] splitFields = fieldPath.split("\\.");
Object curData = data;
for (String splitField : splitFields) {
if (Objects.isNull(curData)) {
return null;
}
if (curData instanceof Map) {
curData = ((Map<?, ?>) curData).get(splitField);
continue;
}
if (curData instanceof List && StringUtils.isNumeric(splitField)
&& Integer.parseInt(splitField) < ((List<?>) curData).size()) {
curData = ((List<?>) curData).get(Integer.parseInt(splitField));
continue;
}
return null;
}
return curData;
}
public Map<String, Object> getData() {
return data;
}
public void putData(String fieldPath, Object data) {
Map<String, Object> updateMap = Maps.newHashMap();
updateMap.put(fieldPath, data);
putData(updateMap);
}
public void putData(Map<String, Object> updateMap) {
updateMap(data, ParamHelper.foldData(updateMap));
}
public void putData(FlowData updateData) {
updateMap(data, updateData.data);
}
public synchronized void removeData(String fieldPath) {
if (StringUtils.isEmpty(fieldPath)) {
return;
}
String[] splitFields = fieldPath.split("\\.");
Object curData = data;
for (int i = 0; i < splitFields.length - 1; i++) {
if (Objects.isNull(curData)) {
return;
}
String splitField = splitFields[i];
if (curData instanceof Map) {
curData = ((Map<?, ?>) curData).get(splitField);
continue;
}
if (curData instanceof List && StringUtils.isNumeric(splitField)
&& Integer.parseInt(splitField) < ((List<?>) curData).size()) {
curData = ((List<?>) curData).get(Integer.parseInt(splitField));
continue;
}
return;
}
String lastSplitField = splitFields[splitFields.length - 1];
if (curData instanceof Map) {
((Map<?, ?>) curData).remove(lastSplitField);
}
if (curData instanceof List && StringUtils.isNumeric(lastSplitField)
&& Integer.parseInt(lastSplitField) < ((List<?>) curData).size()) {
((List<?>) curData).remove(Integer.parseInt(lastSplitField));
}
}
private synchronized void updateMap(Map<String, Object> mainMap, Map<String, Object> updateMap) {
if (Objects.isNull(mainMap)) {
return;
}
for (Entry<String, Object> entry : updateMap.entrySet()) {
String key = entry.getKey();
Object updateValue = entry.getValue();
if (mainMap.containsKey(key)) {
Object mainValue = mainMap.get(key);
if (mainValue instanceof Map && updateValue instanceof Map) {
updateMap((Map<String, Object>) mainValue, (Map<String, Object>) updateValue);
continue;
}
if (mainValue instanceof List && updateValue instanceof List) {
mainMap.put(key, updateValue);
continue;
}
}
mainMap.put(key, updateValue);
}
}
/**
* 找到最后一个有效路径
*/
private String getValidPath(String fieldPath) {
if (Objects.isNull(data)) {
return null;
}
List<String> result = Lists.newArrayList();
String[] splitFields = fieldPath.split("\\.");
Object curData = data;
for (String field : splitFields) {
Object nextData = null;
if (curData instanceof Map) {
nextData = ((Map<?, ?>) curData).get(field);
}
if (curData instanceof List && StringUtils.isNumeric(field)) {
nextData = ((List<?>) curData).get(Integer.parseInt(field));
}
if (Objects.isNull(nextData)) {
break;
}
result.add(field);
curData = nextData;
}
if (CollectionUtils.isEmpty(result)) {
return null;
}
return StringUtils.join(result, ".");
}
@Override
public String toString() {
return JSON.toJSONString(data);
}
}
3. 测试方法
public class Test {
public static void main(String[] args) throws Exception {
FlowData param = new FlowData();
param.putData("vendor_id", 1L);
param.putData("xxx.yyy", "1");
param.putData("shards", "123,11");
System.out.println(JSON.toJSON(param));
ExpressExecuteHandler executeHandler = ExpressExecuteHandler.getInstance();
String ep = "!stringEquals(xxx.yyy,\"-124\") && len(convertToList(shards))>1;";
ExpressBuildHandler.ExpressInfo expressInfo = ExpressBuildHandler.convertExpress(ep, param);
Object result = executeHandler.calculateForResult(expressInfo);
System.out.println(result);
}
}
网友评论