QLExpress基本语法
一、背景介绍
由阿里的电商业务规则、表达式(布尔组合)、特殊数学公式计算(高精度)、语法分析、脚本二次定制等强需求而设计的一门动态脚本引擎解析工具。
在阿里集团有很强的影响力,同时为了自身不断优化、发扬开源贡献精神,于2012年开源。
QLExpress脚本引擎被广泛应用在阿里的电商业务场景,具有以下的一些特性:
- 1、线程安全,引擎运算过程中的产生的临时变量都是threadlocal类型。
- 2、高效执行,比较耗时的脚本编译过程可以缓存在本地机器,运行时的临时变量创建采用了缓冲池的技术,和groovy性能相当。
- 3、弱类型脚本语言,和groovy,javascript语法类似,虽然比强类型脚本语言要慢一些,但是使业务的灵活度大大增强。
- 4、安全控制,可以通过设置相关运行参数,预防死循环、高危系统api调用等情况。
- 5、代码精简,依赖最小,250k的jar包适合所有java的运行环境,在android系统的低端pos机也得到广泛运用。
二、依赖和调用说明
1 2 3 4 5
| <dependency> <groupId>com.alibaba</groupId> <artifactId>QLExpress</artifactId> <version>3.2.0</version>Valine </dependency>
|
1 2 3 4 5 6 7 8
| ExpressRunner runner = new ExpressRunner(); DefaultContext<String, Object> context = new DefaultContext<String, Object>(); context.put("a", 1); context.put("b", 2); context.put("c", 3); String express = "a + b * c"; Object r = runner.execute(express, context, null, true, false); System.out.println(r);
|
三、项目使用
ExpressService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| public interface ExpressService {
Object execute(String expressString, Map<String, Object> context);
Object execute(String expressString, Object valueParam);
}
@Component public class ExpressServiceImpl implements ExpressService {
private final static Logger LOGGER = LoggerFactory.getLogger(ExpressServiceImpl.class);
private final static ExpressRunner expressRunner;
static { DynamicParamsUtil.supportDynamicParams = true; ExpressRunner runner = new ExpressRunner(true, false);
expressRunner = runner; }
@Override public Object execute(String expressString, Map<String, Object> context) { Assert.notNull(context,"method execute param context is null"); Object value = null; if(StringUtils.isNotBlank(expressString)){ DefaultContext<String, Object> contextMap = new DefaultContext<String, Object>(); for(Map.Entry<String,Object> entry : context.entrySet()){ contextMap.put(entry.getKey(), entry.getValue()); } try{ value = expressRunner.execute(expressString, contextMap, null, true, false); }catch (Exception e){ LOGGER.error("call expressRunner error expressString is : {}, context :{}", expressString, context, e); throw new RuntimeException(String.format("表达式{%s}计算错误", expressString)); } } return value; }
@Override public Object execute(String expressString, Object valueParam) { Assert.notNull(valueParam,"method execute context valueParam is null");
Map<String, Object> map = new ObjectMapper().convertValue(valueParam, new TypeReference<Map<String, Object>>() {}); return this.execute(expressString,map); } }
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| @SpringBootTest @RunWith(SpringJUnit4ClassRunner.class) public class ExpressServiceTest {
private final static Logger LOGGER = LoggerFactory.getLogger(ExpressServiceTest.class);
@Autowired private ExpressService expressService;
@Test public void testExecute0(){ String express = "3452";
Map<String, Object> map = new HashMap<>(4); map.put("amount1",0.01); map.put("amount2",1.10);
Object value = this.expressService.execute(express, map);
LOGGER.info("输出类型:{} 结果:{}", value.getClass(), value);
value = this.expressService.execute(express, map);
LOGGER.info("输出类型:{} 结果:{}", value.getClass(), value);
}
@Test public void testExecute1(){ String express = "(amount1+amount2)*100";
Map<String, Object> map = new HashMap<>(4); map.put("amount1",0.01); map.put("amount2",1.10);
Object value = this.expressService.execute(express, map);
LOGGER.info("输出类型:{} 结果:{}", value.getClass(), value);
Assert.assertEquals(new BigDecimal("111.00"), value); }
}
|