SpringEL 应用场景_spring spel foreach-程序员宅基地

技术标签: spring  java  

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 学习

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_35461948/article/details/110197020

智能推荐

ORACLE-SQL(一)-程序员宅基地

文章浏览阅读77次。 迁移时间:2017年6月1日10:02:43CreateTime--2017年6月1日09:59:30Author:Marydon一、SQL语句  (一)基础篇    1.1.1 where 子句      1.1.1.1 where后面可以跟多个条件表达式,表达式之间用and或or连接--查询除去emp表前5条的员工信息(即rownum&gt;5) ..._查询部门编号是10,20,50的人的员工编号

mac下安装thrift踩坑_macm1安装thrift时,执行make报错make[4]: nothing to be done-程序员宅基地

文章浏览阅读1.3k次。今天在安装thrift的时候有种回到了当初刚开始学编程的那种状态,配个环境折腾老半天。本来就在idl里面改动了3行代码,想重新生成一下,确认了该项目之前使用thrift 0.10.0生成后,不得不将已经安装的thrift 0.12.0换掉。本来想用brew install thrift简单安装一下的,但是brew search thrift之后发现只有0.9.0版本的,于是不得不下载源码,手动..._macm1安装thrift时,执行make报错make[4]: nothing to be done for `all-am'xcode-

指定mysql jdbctype_MyBatis JdbcType 与Oracle、MySql数据类型对应关系说明-程序员宅基地

文章浏览阅读315次。1. Mybatis JdbcType与Oracle、MySql数据类型对应列表MybatisJdbcTypeOracleMySqlJdbcTypeARRAYJdbcTypeBIGINTBIGINTJdbcTypeBINARYJdbcTypeBITBITJdbcTypeBLOBBLOBBLOBJdbcTypeBOOLEANJdbcTypeCHARCHARCHARJdbcTypeCLOBCLOBCL..._oracle数据库的的text类型的字段,jdbctype是什么类型

Visual Studio Code 配置 C/C++ 运行环境 - 搭配 MinGW 完美 GNU 配置 Editor_gnu::pure visual studio-程序员宅基地

文章浏览阅读2k次,点赞7次,收藏26次。Visual Studio Code 配置 C/C++ 运行环境 - 搭配 MinGW 完美 GNU 配置前言简介MinGWMinGW 下载安装MinGW 系统环境配置Visual Studio CodeVisual Studio Code 安装以及其基本 C/C++ 插件安装Visual Studio Code C/C++ 环境配置功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入..._gnu::pure visual studio

centos 7 下双网卡bond 与 trunk口的交换机通信(用作KVM虚拟机)_centos7网口trunk-程序员宅基地

文章浏览阅读3.2k次,点赞3次,收藏8次。centos 7 下双网卡bond 与 trunk口的交换机通信做这个首先要明白什么是trunk口trunk 是实现不同网络设备的不同Vlan之间的通信 so 网卡也属于网络设备设置vlan后也可以通信先说下我的环境网络工程师在两台交换机上做了两个trunk模式的vlan 两个口分别连接服务器的 1网卡和2网卡这样做bond 可以防止一台交换机损坏的容灾..._centos7网口trunk

数据存储(k8s)-程序员宅基地

文章浏览阅读58次。容器的生命周期可能很短,会被频繁地创建和销毁。那么容器在销毁时,保存在容器中的数据也会被清除。这种结果对用户来说,在某些情况下是不乐意看到的。为了持久化保存容器的数据,kubernetes引入了Volume的概念。Volume是Pod中能够被多个容器访问的共享目录,它被定义在Pod上,然后被一个Pod里的多个容器挂载到具体的文件目录下,kubernetes通过Volume实现同一个Pod中不同容器之间的数据共享以及数据的持久化存储。Volume的生命容器不与Pod中单个容器的生命周期相关,

随便推点

SoapUI接口测试实例(webservice接口)-程序员宅基地

文章浏览阅读745次。接口测试步骤注:以测试queryHistoryAccepts接口作举例。1、用户登录获取SessionKey实体信息注:由于大部分的接口都需要SessionKey实体的信息,因此测试那些接口都需要先调用用户登录接口,用于获取SessionKey实体下的信息。在接口显示窗口找到CommonServiceSoap11Binding下的CheckCallerPermision..._soaoui测试的接口 返回的raw信息 server是unknown

文心一言插件开发(第二篇-程序员宅基地

文章浏览阅读887次,点赞19次,收藏14次。name_for_human”,平台内全局唯一标识,后缀数字建议长且随机,更不容易重名冲突,这里改成了"单词本_TianJi"“name_for_model”,这里改成了"wordbook_TianJi"官网提供了一套示例demo文件。也可以把修改文件后的压缩包上传。回到文心一言,点击本地调试。,提取码: q7xa。

SAP CRM 创建销售订单时报错 - CRM_ORDER_MISC 020-程序员宅基地

文章浏览阅读387次。(2) 检查这篇 wiki 里介绍的步骤:https://wiki.scn.sap.com/wiki/display/CRM/Partner+conversion+error+in+sales+or+service+documents+CRM-BF-PD。这个朋友采取的解决方案就是将订单的 contact person 字段删除,再重新维护,然后错误就消失了。这是一个 CRM 系统和远端系统(ERP 系统?) 进行数据传输时出的问题。错误消息:复制文件时,系统 PRDCLNT800 中发生错误。

Leetcode 14. Longest Common Prefix-程序员宅基地

文章浏览阅读36次。https://leetcode.com/problems/longest-common-prefix/class Solution {public: string longestCommonPrefix(vector<string>& strs) { if(strs.empty()) return ""; string ans=st...

jenkins构建定时测试任务报错----ImportError: No module named 'unittest2'-程序员宅基地

文章浏览阅读1.3k次。直接执行脚本是没有问题,报如下错误:File “test_HTML_run_all.py”, line 4, in import unittest2ImportError: No module named ‘unittest2’_no module named 'unittest2

unity3D游戏开发四之创建基本游戏场景一_unity场景一个人游戏一般做多少个-程序员宅基地

文章浏览阅读1.8w次。Unity创建游戏的理念可以被jian'da_unity场景一个人游戏一般做多少个

推荐文章

热门文章

相关标签