1 Star 0 Fork 33

yasepix / jfireEL

forked from eric_1989 / jfireEL 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
README.md 6.83 KB
一键复制 编辑 原始数据 按行查看 历史
eric 提交于 2020-02-13 17:34 . 更新ReadMe

高性能EL表达式解析框架 jfireEL

使用场景

EL 表达式解析,在一些涉及到动态规则配置的场合使用较多。比如工作流引擎中的外部规则注入,比如 Spring 框架中的 SpEL 解析等等。jfireEL 框架支持常见的 EL 表达式,诸如四则运算,数组元素获取,链表元素获取,Map 键值对获取,属性获取,方法调用,级联调用,支持获取类定义,枚举定义,支持获取对象等等。

相比于 SpringEL,jfireEL 在表达上更为简洁,比如对于 SpringEL,一个变量属性级联对比的表达式可以写为vars['home'].bool(vars['person'].getAge() + '12' != vars['value']),而jfireEL 的写法直接为home.bool(person.getAge() + '12' != value)。显然 jfireEL 的可读性更高。

欢迎加入技术交流群186233599讨论交流,也欢迎关注笔者公众号:风火说。

性能测试

使用 EL 表达式vars['home'].bool(vars['person'].getAge() + '12' != vars['value'])对jfireEL,BeetlEL,SpringEL 三款框架进行性能对比验证。在进入测试之前均预热相同次数确保初始化完毕。三款框架性能对比如下

使用方式

首先引入Maven依赖,如下

<dependency>
    <groupId>com.jfirer</groupId>
    <artifactId>jfireEl</artifactId>
    <version>1.0</version>
</dependency>

在代码中使用时,首先进行EL 表达式的解析,而后使用这个外部参数就可以对表达式进行求值。

int[] array = new int[]
{
    1, 2, 3, 4
};
Map < String, Object > vars = new HashMap < String, Object > ();
vars.put("array", array);
Expression lexer = Expression.parse("array[2]");//使用静态方法parse解析字符串形式的EL表达式,得到一个表达式实例。该表达式是一个并发安全的实例,可以供后续反复的并发的调用。一次生成即可,无需反复生成。
assertEquals(3, lexer.calculate(vars));//外部参数通过Map的形式传递。使用方法calculate根据给定的参数计算得到表达式的值

在 EL 表达式中,如果一个字符串使用''进行包围,则被认为是一个字符串;否则的话,可以被认定为变量名,方法名,参数名,具体某个枚举的名称,视上下文的情况而定。

jfireEL 使用 Map 作为参数的载体,其 Key 的值就是变量名,value 对应的就是变量值。

使用示例

具体的代码使用方法可以参见如下示例。

[] 获取元素支持

jfireEL 支持使用[]对数组元素,List,Map进行属性的获取,比如下面的表达式都是正确且合法的。

获取数组元素

int[] array = new int[]
{
    1, 2, 3, 4
};
Map < String, Object > vars = new HashMap < String, Object > ();
vars.put("array", array);
Expression lexer = Expression.parse("array[2]"); //EL 表达式解析
assertEquals(3, lexer.calculate(vars)); //EL 表达式执行

获取List中元素

List < String > list = new ArrayList < String > ();
list.add("1212");
list.add("13");
Map < String, Object > vars = new HashMap < String, Object > ();
vars.put("list", list);
Expression lexer = Expression.parse("list[1]");
assertEquals("13", lexer.calculate(vars));

获取Map键值

Map < String, String > map = new HashMap < String, String > ();
map.put("1", "12");
Map < String, Object > vars = new HashMap < String, Object > ();
vars.put("map", map);
vars.put("age", "1");
Expression lexer = Expression.parse("map['1']");
assertEquals("12", lexer.calculate(vars));
Expression lexer2 = Expression.parse("map[age]");
assertEquals("12", lexer2.calculate(vars));

四则运算支持

四则运算,加减乘除,求余的支持如下

Expression lexer = Expression.parse("1+4/2");
assertEquals(3, lexer.calculate(null)); //对于不需要注入参数计算的场合,入参可以直接为null,不影响运算
Expression lexer = Expression.parse("5-(4-1)");
assertEquals(2, lexer.calculate(null));
Expression lexer = Expression.parse("1*2-1");
assertEquals(1, lexer.calculate(null));

对象属性获取和类属性获取支持

支持使用.进行对象属性获取。而如果要获取静态类的属性,支持使用T(ClassName)的形式获取静态类,再使用.来获取对应的属性。两者的代码如下

public class PropertyTest extends TestSupport
{
    public static int age = 12;
    //对象属性获取
    @Test
    public void test()
        {
            Expression lexer = Expression.parse("home.person.age");
            //home 是一个对象,包含一个对象属性 person,age是person中的一个数字属性
            assertEquals(person.age, lexer.calculate(vars));
            lexer = Expression.parse("home.person.age", Functional.build().setPropertyFetchByUnsafe(true).setRecognizeEveryTime(false).toFunction());
            assertEquals(person.age, lexer.calculate(vars));
        }
        
    //类属性获取
    @Test
    public void test2()
    {
        //PropertyTest 就是测试类本身,使用T()包围表明这是一个类的全限定名
        int result = Expression.parse("T(PropertyTest).age").calculate();
        assertEquals(age, result);
    }
}

上面的对象属性获取可以看到,jfireEL 框架支持级联操作

三元表达式支持

jfireEL 支持 expression?value1:value2 这种形式的三元表达式,具体代码如下

assertEquals(1, Expression.parse("2>1?1:2").calculate());
assertEquals(1, Expression.parse("3-2+1>1?1:2").calculate());
assertEquals(1, Expression.parse("3-2+1>1?2-1:2").calculate());
assertEquals(2, Expression.parse("3-2+1<1?2:1+1").calculate());
Map < String, Object > vars = new HashMap < String, Object > ();
Map < String, Object > param = new HashMap < String, Object > ();
param.put("b", false);
vars.put("map", param);
assertEquals(2, Expression.parse("map['b']?3:1*2").calculate(vars));
assertEquals(4, Expression.parse("(map['b']?3:2)*2").calculate(vars));

方法调用获取

方法调用也是使用.作为开始符号,和属性获取不同的是,其需要使用()作为结束符号,并且可以根据情况,支持入参填入。具体代码如下

Expression lexer = Expression.parse("home.person.getAge()");
assertEquals(person.age, lexer.calculate(vars));
Expression lexer = Expression.parse("home.personAge2(person.getAge()+2)");//方法调用支持入参,并且支持在入参中执行计算
assertEquals(person.getAge() + 2, lexer.calculate(vars));

枚举类支持

jfireEL 支持获取具体的枚举实例,也获取类属性的方式相同,代码如下

public class EnumTest
{
    enum Name
    {
        dd;
    }

    @Test
    public void test()
    {
        Expression lexer = Expression.parse("T(EnumTest$Name).dd");
        assertEquals(Name.dd, lexer.calculate());
    }
}
Java
1
https://gitee.com/yasepix/jfireEL.git
git@gitee.com:yasepix/jfireEL.git
yasepix
jfireEL
jfireEL
master

搜索帮助