今天,在做功能的时候,遇到一个问题。
xml文件中有配置限制条件,例如:
<CONDITION TYPE="range$in"
PARASNAME="userlevel$department"
EXPRESSION="(userlevel) and (department)">
[0,4]$[开发部][测试部]
</CONDITION>
其中,PARASNAME 中配置的是变量名称; TYPE用来描述变量和值的关系;EXPRESSION 是各个变量的逻辑关系。
需要实现这样一个功能:
根据前台传入的变量值,以及EXPRESSION中配置的表达式的逻辑关系,计算出当前情况是否满足条件,然后返回一个结果。
初步想法是,把 EXPRESSION 中的内容按如下规则进行替换:
变量–>变量和值的关系
and -->&&
就像这样:
(userlevel>=0&&userlevel<=4)&&
([开发部][测试部].indexOf(department)>0)
但问题是,替换之后的表达式如何执行呢?
通过查询,还真有可以执行表达式的方法。一般通过jexl2 方式。
首先引入jar包:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-jexl</artifactId>
<version>2.0</version>
</dependency>
其次,写好执行表达式的函数:
/*
exp:表达式字符串
map:表达式中的变量和值
*/
public static Object exeExpression(String exp,Map<String,Object> map){
JexlEngine jexl=new JexlEngine();
Expression e = jexl.createExpression(exp);
JexlContext jc = new MapContext();
for(String key:map.keySet()){//copy一下参数Map到JexlContext
jc.set(key, map.get(key));
}
if(e.evaluate(jc)==null){
return "";
}
return e.evaluate(jc);
}
}
调用方法:
Map<String,Object> map=new HashMap<String,Object>();
map.put("userlevel","1");
map.put("department","aaa");
String expression=
"(userlevel>=0&&userlevel<=4)&&([开发部][测试部].indexOf(department)>0)";
Object code = convertToCode(expression,map);
System.out.println(""+code);
执行函数报错:
Exception in thread "main" org.apache.commons.jexl2.JexlException: com.test.TestReg.convertToCode@37tokenization failed
at org.apache.commons.jexl2.JexlEngine.parse(JexlEngine.java:816)
at org.apache.commons.jexl2.JexlEngine.createExpression(JexlEngine.java:380)
at org.apache.commons.jexl2.JexlEngine.createExpression(JexlEngine.java:364)
at com.test.TestReg.convertToCode(TestReg.java:37)
at com.test.TestReg.main(TestReg.java:20)
Caused by: org.apache.commons.jexl2.parser.TokenMgrError: Lexical error at line 1, column 32. Encountered: "\u5f00" (24320), after : ""
at org.apache.commons.jexl2.parser.ParserTokenManager.getNextToken(ParserTokenManager.java:1564)
看提示的意思是有汉字,哦,忘记了给字符串加引号了。
又改成如下:
String expression="(userlevel>=0&&userlevel<=4)&&(\"[开发部][测试部]\".indexOf(department)>0)";
还是报同样的错误。
后来换了个思路,把这些常量放到放到变量Map里。
map.put("depart","[开发部][测试部]");
String expression="(userlevel>=0&&userlevel<=4)&&(depart.indexOf(department)>0)";
输出结果:
false
问题解决,原来jexl2表达式不支持字符串常量。
通过研究 jexl2官网 发现:
1.构建参数值的Map可以优化一下,简化代码:
/*
exp:表达式字符串
map:表达式中的变量和值
*/
public static Object exeExpression(String exp,Map<String,Object> map){
JexlEngine jexl=new JexlEngine();
Expression e = jexl.createExpression(exp);
JexlContext jc = new MapContext(map);
if(e.evaluate(jc)==null){
return "";
}
return e.evaluate(jc);
}
}
2 创建JEXL engine时,最好用单例模式。(可能是3.0版本才支持)
private static final JexlEngine jexl =
new JexlBuilder.cache(512).strict(true).silent(false).create();