SpringEL 应用场景

SpringEL 应用场景

 

目录

场景一: 计算值

1,单位的转换

把 毫秒的值,转换为分钟

把bytes的值转成Gb

2,数值之间的计算

场景:

初始化话配置数据

初始化数据:

进行计算处理:

输出:

小结:

场景二:替换值

1,使用SpringEL 进行替换:

2,另一种替换方式: replaceByStrSubstitutor

3,正则表达替换方式: 

 

为什么要使用SpringEL?      

SpringEL可以进行赋值再进行计算,功能还是很强大的。 

场景一: 计算值

1,单位的转换

把 毫秒的值,转换为分钟

long us = 234567;
EvaluationContext context = new StandardEvaluationContext(); // 表达式的上下文
ExpressionParser parser = new SpelExpressionParser();
context.setVariable("value", us); // 为了让表达式可以访问该对象, 先把对象放到上下文中
Object ss = parser.parseExpression("#value/(60*60)").getValue(context);
System.out.println("minutes: " + ss);

把bytes的值转成Gb

long bytes = 53456712312L;

EvaluationContext context = new StandardEvaluationContext(); // 表达式的上下文

ExpressionParser parser = new SpelExpressionParser();

context.setVariable("value", bytes); // 为了让表达式可以访问该对象, 先把对象放到上下文中

Object ss = parser.parseExpression("#value/(1024*1024*1024)").getValue(context);

System.out.println("Gb size: " + ss);

2,数值之间的计算

场景:

获取到订单的总数(orderTotalCount),超过10分钟的数量(orderDurationOverTenMinute),点击数(orderClicks),耗时(秒)(orderDealDuration)

 

目标计算出订单中: 超过10分钟的占比(orderOverTenMinuteRate),平均点击数(orderAvgClicks),平均耗时(分钟)(orderAvgDealDuration)

 

公式:

超过10分钟的占: orderOverTenMinuteRate =  orderDurationOverTenMinute / orderTotalCount

平均点击数:  orderAvgClicks =  orderClicks / orderTotalCount

平均耗时(分钟)  orderAvgDealDuration = orderDealDuration / orderTotalCount * 100

这些一般配置在表格,这边就直接模拟数据

考虑到这边有值之间的相互计算,SpringEL 替换值,再进行计算,考虑使用SpringEL 来解决。

初始化话配置数据


 

public static List<EsQueryFormula> initEsFormulaData() {
    String orderDurationOverTenMinuteStr = "{\"kpiKey\":\"orderDurationOverTenMinute\",\"pattern\":\"#\",\"unit\":\"\",\"formula\":\"#orderDurationOverTenMinute\"}";
    EsQueryFormula orderDurationOverTenMinute = JSON.parseObject(orderDurationOverTenMinuteStr, EsQueryFormula.class);
    String orderTotalCountStr = "{\"kpiKey\":\"orderTotalCount\",\"pattern\":\"#\",\"unit\":\"\",\"formula\":\"#orderTotalCount\"}";
    EsQueryFormula orderTotalCount = JSON.parseObject(orderTotalCountStr, EsQueryFormula.class);
    String orderAvgClicksStr = "{\"kpiKey\":\"orderAvgClicks\",\"pattern\":\"#\",\"unit\":\"\",\"formula\":\" #orderTotalCount>0?#orderClicks/#orderTotalCount:0\"}";
    EsQueryFormula orderAvgClicks = JSON.parseObject(orderAvgClicksStr, EsQueryFormula.class);
    String orderOverTenMinuteRateStr = "{\"kpiKey\":\"orderOverTenMinuteRate\",\"pattern\":\"#.##\",\"unit\":\"%\",\"formula\":\"#orderTotalCount>0?#orderDurationOverTenMinute/#orderTotalCount*100:0\"}";
    EsQueryFormula orderOverTenMinuteRate = JSON.parseObject(orderOverTenMinuteRateStr, EsQueryFormula.class);
    String orderAvgDealDurationStr = "{\"kpiKey\":\"orderAvgDealDuration\",\"pattern\":\"#.##\",\"unit\":\"min\",\"formula\":\"#orderTotalCount>0?#orderDealDuration/#orderTotalCount/60:0\"}";
    EsQueryFormula orderAvgDealDuration = JSON.parseObject(orderAvgDealDurationStr, EsQueryFormula.class);
    List<EsQueryFormula> esQueryFormulaList = new ArrayList<>();
    esQueryFormulaList.add(orderDurationOverTenMinute);
    esQueryFormulaList.add(orderTotalCount);
    esQueryFormulaList.add(orderAvgClicks);
    esQueryFormulaList.add(orderOverTenMinuteRate);
    esQueryFormulaList.add(orderAvgDealDuration);
    return esQueryFormulaList;
}

// 配置实体类

public class EsQueryFormula {
    private String kpiKey;
    private String pattern;
    private String unit;
    private String formula;

    public EsQueryFormula(String kpiKey, String pattern, String unit, String formula) {
        this.kpiKey = kpiKey;
        this.pattern = pattern;
        this.unit = unit;
        this.formula = formula;
    }

    public String getKpiKey() {
        return kpiKey;
    }

    public void setKpiKey(String kpiKey) {
        this.kpiKey = kpiKey;
    }

    public String getPattern() {
        return pattern;
    }

    public void setPattern(String pattern) {
        this.pattern = pattern;
    }

    public String getUnit() {
        return unit;
    }

    public void setUnit(String unit) {
        this.unit = unit;
    }

    public String getFormula() {
        return formula;
    }

    public void setFormula(String formula) {
        this.formula = formula;
    }

    @Override
    public String toString() {
        return "EsQueryFormula{" +
                "kpiKey='" + kpiKey + '\'' +
                ", pattern='" + pattern + '\'' +
                ", unit='" + unit + '\'' +
                ", formula='" + formula + '\'' +
                '}';
    }
}

初始化数据:

private static List<Map<String, Double>> initData(){
    // 模拟 四个指标的数据: orderDurationOverTenMinute , orderTotalCount, orderClicks, orderDealDuration
    List<Map<String, Double>> data = new ArrayList<>();
    Map<String, Double> first = new HashMap<>();
    first.put("orderDurationOverTenMinute", 10d);
    first.put("orderTotalCount", 88d);
    first.put("orderClicks", 364d);
    first.put("orderDealDuration", 123654d);

    Map<String, Double> second = new HashMap<>();
    second.put("orderDurationOverTenMinute", 69d);
    second.put("orderTotalCount", 188d);
    second.put("orderClicks", 964d);
    second.put("orderDealDuration", 2236541d);

    Map<String, Double> third = new HashMap<>();
    third.put("orderDurationOverTenMinute", 97d);
    third.put("orderTotalCount", 151d);
    third.put("orderClicks", 535d);
    third.put("orderDealDuration", 183659d);

    data.add(first);
    data.add(second);
    data.add(third);

    return data;
}

进行计算处理:

private static final ExpressionParser PARSER = new SpelExpressionParser();



public static void main(String[] args) {

    testElCalculate();

}



public static void testElCalculate(){

    List<Map<String, Double>> dataList = initData();

    List<EsQueryFormula> esQueryFormulaList = initEsFormulaData();

    List<Map<String, Object>> aimList = ListUtils.emptyIfNull(dataList).stream().map(e -> getEsQueryMap(esQueryFormulaList, e)).collect(Collectors.toList());

    aimList.forEach(System.out::println);
}


// 计算值
public static Map<String, Object> getEsQueryMap(List<EsQueryFormula> esQueryFormulaList, Map<String, Double> dataMap) {
    return ListUtils.emptyIfNull(esQueryFormulaList).stream()
            .collect(Collectors.toMap(EsQueryFormula::getKpiKey, e -> getEsQueryExpress(e, dataMap), (x, y) -> x));
}

public static String getEsQueryExpress(EsQueryFormula esQueryFormula, Map<String, Double> dataMap) {
    String unit = esQueryFormula.getUnit();
    return getExpressionResult(dataMap, esQueryFormula) + (unit != null ? unit : "");
}

private static String getExpressionResult(Map<String, Double> dataMap, EsQueryFormula esQuery) {
    return parseExpression(dataMap, esQuery.getFormula(), esQuery.getPattern());
}

public static String parseExpression(Map<String, Double> data, String formula, String pattern) {
    Double expressionResult = parseDoubleExpression(data, formula);
    if (Objects.nonNull(expressionResult)) {
        return decimalFormat(expressionResult, pattern);
    }
    return null;
}

private static String decimalFormat(Double value, String pattern) {
    DecimalFormat df = new DecimalFormat(pattern);
    df.setRoundingMode(RoundingMode.HALF_UP);
    return df.format(value);
}


public static Double parseDoubleExpression(Map<String, Double> data, String formula) {
    Expression expression = PARSER.parseExpression(formula);
    return parseDoubleExpression(data, expression);
}

public static Double parseDoubleExpression(Map<String, Double> data, Expression expression) {
    EvaluationContext ctx = new StandardEvaluationContext();
    if (Objects.nonNull(data)) {
        data.forEach(ctx::setVariable);
    }
    return expression.getValue(ctx, Double.class);
}

输出:

{orderDurationOverTenMinute=10, orderAvgDealDuration=23.42min, orderAvgClicks=4, orderTotalCount=88, orderOverTenMinuteRate=11.36%}

{orderDurationOverTenMinute=69, orderAvgDealDuration=198.27min, orderAvgClicks=5, orderTotalCount=188, orderOverTenMinuteRate=36.7%}

{orderDurationOverTenMinute=97, orderAvgDealDuration=20.27min, orderAvgClicks=4, orderTotalCount=151, orderOverTenMinuteRate=64.24%}

小结:

  回到开头的提问:为什么要使用SpringEL?

 因为SpringEL可以进行赋值再进行计算,上面的场景刚好就是用了SpringEL这个强大的功能,解决了问题。再遇到类似需要赋值,计算的场景,就可以把SpringEL用起来了。

场景二:替换值

1,使用SpringEL 进行替换:

比如es的查询串:

"{\"size\":#{#size},\"from\":#{#from},\"query\":{\"bool\":"

        + "{\"must\":[{\"range\":{\"timeStamp\":{\"gte\":\"#{#startDate}\","

        + "\"lte\":\"#{#endDate}\",\"format\":\"yyyy-MM-dd HH:mm:ss\","

        + "\"time_zone\":\"+00:00\"}}}]}}}";

最直接的方式是用 StringUtils 的方法 replace() 挨个进行替换。

如果使用SpringEL 怎么进行替换呢?

/**
     *  SpringEL 的基本语法   #{}, 值之前加#,比如 #size
     */
    public static void replaceByEl() {
        String esStr = "{\"size\":#{#size},\"from\":#{#from},\"query\":{\"bool\":" +
                "{\"must\":[{\"range\":{\"timeStamp\":{\"gte\":\"#{#startDate}\",\"lte\":\"#{#endDate}\"," +
                "\"format\":\"yyyy-MM-dd HH:mm:ss\",\"time_zone\":\"+00:00\"}}}]}}}";
        System.out.println(esStr);

        Map<String,Object> paramMap = new HashMap<>();
        paramMap.put("size", 20);
        paramMap.put("from", 0);
        paramMap.put("startDate", "2020-11-22 00:00:00");
        paramMap.put("endDate", "2020-11-25 00:00:00");
        String esSearchStr = createEsQuery(esStr, paramMap);
        System.out.println("esSearchStr EL  "+esSearchStr);

    }

    public static String createEsQuery(String esQueryStr, Map<String, Object> map) {
        ExpressionParser parser = new SpelExpressionParser();
        // TemplateParserContext 处理  这个的符号 this("#{", "}");
        Expression expression = parser.parseExpression(esQueryStr, new TemplateParserContext());
        EvaluationContext ctx = new StandardEvaluationContext();
        StandardEvaluationContext ctx1 = new StandardEvaluationContext();
        //
//        public void setVariables(Map<String, Object> variables) {
//            variables.forEach(this::setVariable);
//        }
        if (Objects.nonNull(map)) {
            // StandardEvaluationContext 可以用两种方式一 setVariable 和 setVariables
            ctx1.setVariables(map);
            // EvaluationContext 只能用一种方式 setVariable
            map.forEach(ctx::setVariable);
        }
        return expression.getValue(ctx, String.class);
    }

2,另一种替换方式: replaceByStrSubstitutor

使用 StrSubstitutor

/**
 * StrSubstitutor 占位符替换
 *   public StrSubstitutor(Map valueMap) {
 *         this(StrLookup.mapLookup(valueMap), DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ESCAPE);
 *     }
 *
 * // Constant for the default escape character.
 *
 *     public static final char DEFAULT_ESCAPE = '$';
 *
 *      // Constant for the default variable prefix.
 *
 *     public static final StrMatcher DEFAULT_PREFIX = StrMatcher.stringMatcher("${");
 *
 *     //   Constant for the default variable suffix.
 *
 *     public static final StrMatcher DEFAULT_SUFFIX = StrMatcher.stringMatcher("}");
 *
 *   基本语法结构 ${}
 */
public static void replaceByStrSubstitutor() {
    String esStr = "{\"size\":${size},\"from\":${from},\"query\":{\"bool\":" +
            "{\"must\":[{\"range\":{\"timeStamp\":{\"gte\":\"${startDate}\",\"lte\":\"${endDate}\"," +
            "\"format\":\"yyyy-MM-dd HH:mm:ss\",\"time_zone\":\"+00:00\"}}}]}}}";
    System.out.println(esStr);

    Map<String,Object> paramMap = new HashMap<>();
    paramMap.put("size", 20);
    paramMap.put("from", 0);
    paramMap.put("startDate", "2020-11-22 00:00:00");
    paramMap.put("endDate", "2020-11-25 00:00:00");
    StrSubstitutor sub = new StrSubstitutor(paramMap);
    String esSearchStr = sub.replace(esStr);
    System.out.println("esSearchStr StrSubstitutor "+esSearchStr);

}

     两个比较起来,StrSubstitutor会简单些。

     在注入值的时候,被占位符挤压,在替换中也被占位符挤压,SpringEL表示:“我好难啊!”

3,正则表达替换方式: 

 public static void main(String[] args) {
        String esStr = "{\"size\":${size},\"from\":${from},\"query\":{\"bool\":" +
                "{\"must\":[{\"range\":{\"timeStamp\":{\"gte\":\"${startDate}\",\"lte\":\"${endDate}\"," +
                "\"format\":\"yyyy-MM-dd HH:mm:ss\",\"time_zone\":\"+00:00\"}}}]}}}";
        System.out.println(esStr);

        Map<String,Object> paramMap = new HashMap<>();
        paramMap.put("size", 20);
        paramMap.put("from", 0);
        paramMap.put("startDate", "2020-11-22 00:00:00");
        paramMap.put("endDate", "2020-11-25 00:00:00");
        // 表达式 $ 开头, { } 匹配这里面的, 里面的是字母
        String pattern = "\\$\\{([a-zA-Z_]*)\\}";
        getMatchReplace(esStr, pattern, paramMap);
    }


    public static void getMatchReplace(String content, String pattern, Map<String, Object> param)
    {
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(content);
        StringBuffer sb = new StringBuffer();
        while (m.find())
        {
            String key = m.group(1);
            String value = MapUtils.getString(param, key);
            m.appendReplacement(sb, value == null ? "" : ("\"").concat(value).concat("\""));
        }
        m.appendTail(sb);
        System.out.println(sb.toString());
    }

结果:

{"size":${size},"from":${from},"query":{"bool":{"must":[{"range":{"timeStamp":{"gte":"${startDate}","lte":"${endDate}","format":"yyyy-MM-dd HH:mm:ss","time_zone":"+00:00"}}}]}}}

{"size":20,"from":0,"query":{"bool":{"must":[{"range":{"timeStamp":{"gte":"2020-11-22 00:00:00","lte":"2020-11-25 00:00:00","format":"yyyy-MM-dd HH:mm:ss","time_zone":"+00:00"}}}]}}}

总结:

      在SpringEL的应用中,基于SpringEL的特性:赋值和计算。尤其是两者结合起来,更显得有优势。赋值(注入和替换值)可以优先考虑占位符(${}),比较简便。需要赋值和计算,考虑使用SpringEL。

 上一篇: SpringEL 学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天狼1222

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值