Git仓库:https://github.com/alibaba/QLExpress
1.示例Demo
1.实体构建
@Data public class User { @QLAlias("姓名") private String name; @QLAlias("年龄") private Integer age; @QLAlias("性别") private String gender; @QLAlias("身高") private Double height; @QLAlias("体重") private Double weight; }
2.接口定义
@Service(value = "userManagerImpl") public class UserManagerImpl implements UserManager { ... ... @Override @QLRule("isAdult") public Boolean isAdult(@QLAlias("用户") User user) { throw new RuntimeException("不应该走到这里"); } @Override @QLRule public BMIResult calculate(@QLAlias("用户") User user) { throw new RuntimeException("不应该走到这里"); } }
3.脚本编写
表达式内容维护在一个yaml里,默认扫描路径为:classpath*:/rules/**/*.yaml
这里为:user.yaml
// 对应上述接口1 isAdult: 用户.年龄 >= 18 // 对应上述接口2 com.test.UserManagerImpl.calculate: | import com.test.Result; bmiResult = new Result(); bmiValue = 用户.体重 / (患者.身高 * 患者.身高); bmiResult.setBmiValue(bmiValue); if (bmiValue < 18.5) { bmiResult.setMsg("体重过低"); } else if (bmiValue > 23.9) { bmiResult.setMsg("体重过高"); } else { bmiResult.setMsg("体重正常"); } return bmiResult;
2.运行环节
QLExpressRunner如下图所示,从语法树分析、上下文、执行过程三个方面提供二次定制的功能扩展。
1.获取原始脚本,参数
- QLRule中的value可以使用缺省值,对应yaml的key则为缺省值对应的ruleCode
- 存在默认读取文件路径:com.c2f.boot.starter.rule.engine.QLExpressProperties
- String rule为获取的原始脚本,后续基于此构建AST语法树
2.构建后续赋值上下文
- 没取别名,默认构建一组:形参:value
- 取别名,另构建一组:别名:value(后续yaml能汉化使用也是基于此)
- 所以默认构建的上下文数量 = 形参个数 * 1,有别名 = 形参个数 * 2
3.调用执行
延迟执行
默认第一次执行即缓存
构建AST语法树
基于:com.ql.util.express.parse.KeyWordDefine4Java 构建
选择匹配工厂
递归解析
分解为Word[]:"sum",”=“,”0“,”;“,"for","(","i",......
Word[]转化为List《ExpressNode》:每一个word变得有意义:常量、变量、符号、分割符号
解析第一行:请领状态 = 药品请领单.执行状态
解析第二行:执行计划状态 = 执行计划.当前执行状态,clearDataStack后续也会当做一个指令使用
解析第三行,不再是loadAttr而是LoadData
或需是引包则loadData,变量定义是LoadAttr,未研究
真正执行
基于不同指令进入不同的重写方法
清除栈内数据
执行完毕返回