美文网首页
【平台化引擎-3】组件工厂—配置化的能力

【平台化引擎-3】组件工厂—配置化的能力

作者: 小胖学编程 | 来源:发表于2024-02-05 13:52 被阅读0次

整体方案

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

而本篇文章重点描述下如何实现平台化的能力。

平台化的组件工厂.png

运行加载流程:


整体方案.png

实现

2.1 引入QlExpress脚本语言

引入依赖:

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>QLExpress</artifactId>
  <version>3.2.0</version>
</dependency>

QLExpress介绍

和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)获取到平台上的配置

两类配置:

  1. lib:jar私服地址+类全名;
  2. 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方法是核心方法:

  1. 完成表达式字符替换(.替换为_);
  2. 完成参数的获取(结构化参数替换为扁平化参数)
  3. 完成脚本函数的构建;
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);
    }
}

相关文章

  • 阿里巴巴arouter组件化总结

    第1节 组件化原因 第2节 组件化须考虑的问题 第3节 ARouter使用3.1 初始化配置3.2 高阶使用1. ...

  • 文档介绍

    CocosCreator是目前流行的主打H5平台的游戏引擎,在 Cocos2d-x 基础上实现了彻底脚本化、组件化...

  • (二)bean的实例化及作用域

    Bean的实例化有三种方式:构造器实例化、静态工厂方式实例化、实例工厂实例化。 1.构造器实例化:通过配置xml文...

  • 中台需求分析笔记

    中台的定义: 基本表现:提炼各个业务线的共性需求; 系统目标:打造成平台化、标准化、组件化的系统能力,通过接口或者...

  • Android组件化专题 - 组件化配置

    demo地址 Android组件化专题,详细讲解组件化的使用及配置,以及实现的原理。 本文章讲解了组件化的由来及配...

  • 终极组件化框架项目方案详解

    目录 1.什么是组件化? 2.为什么需要组件化和组件化带来的好处? 3.组件化的基本框架 4.组件化框架的具体实现...

  • 终极组件化框架项目方案详解

    目录 1.什么是组件化? 2.为什么需要组件化和组件化带来的好处? 3.组件化的基本框架 4.组件化框架的具体实现...

  • Android快速迭代设计与实践

    目录 基础建设 基础建设 组件化,插件化,跨平台,壳view组件化:便于修改,提供其它APP使用;插件化:各个业务...

  • Vue 组件化开发

    组件化开发思想 现实中的组件化思想体现(1)标准(2)分治(3)重用(4)组合 编程中的组件化思想体现 组件化规范...

  • Android启动页出现白屏、黑屏的解决方案

    项目使用的是组件化,需要在初始化时解析各组件对配置文件,然后分发配置文件初始化组件,这些繁杂操作得在applica...

网友评论

      本文标题:【平台化引擎-3】组件工厂—配置化的能力

      本文链接:https://www.haomeiwen.com/subject/gwhxadtx.html