企业信息化系统核心在于业务领域的概念模型及于此基础上复杂多变的业务规则,实现中通常抽象规则的接口方法,使用继承或策略等设计模式实现不同的业务规则的实现。领域的概念模型在特定领域是稳定的,比如,采购场景下的报价模型包括是:报价供应商、价格、税率、汇率、报价标的物有效数量、有效地点、有效时间、送货时间等业务概念。从定标规则,从众多报价中选择最终报价的规则则在不同企业,不同商品都有则不同,如按最低价、按最高价(卖出场景)、按最快送货、按供应商评分等等可能的选择方式,在每次规则变化的时候重新迭代实现是一种方式,编写业务规则并支持动态替换执行是另一种选择。 本文介绍了Java实现动态业务规则几种方法。
基本流程如下:
Spring 中动态加载类替换Bean的一个示例过程:
//加载新的实现类 Class> type = loadClass(classPath, className); //销毁原注册旧bean listableBeanFactory.destroySingleton(beanName); //创建新实现类的实例 T obj = listableBeanFactory.createBean(type); //注册新的实例到容器中 listableBeanFactory.registerSingleton(beanName, obj); |
Java 内置的Nashorn引擎支持在Java中执行JavaScript 代码。ScriptEngineManager是Nashorn引擎的API。使用方法如下:
示例代码如下:
ScriptEngineManager manager = new ScriptEngineManager(); //通过后缀名获取引擎, getEngineByxxx方法提供了通过不同方式获取引擎的方法 ScriptEngine engine = manager.getEngineByExtension("js"); //设置参数 engine.put("param1", "test"); Compilable compEngine = (Compilable) engine; ompiledScript script = compEngine.compile(" var output = param1; return output; "); //执行脚本并获取结果 String result = script.eval() |
引擎也支持将脚本编译为方法供Java代码调用,但从耦合角度并不是很好的实践,脚本应尽可能的实现单一职能、独立的规则。而不是耦合到一个复杂逻辑中。
表达式引擎是根据传入参数执行计算公式或特定表达式并返回结果的工具。表达式引擎通道都使用类java的语法规则,可以方便的和java运行时不同类型的数据对象进行交互,强调高效的性能。另外也有源自金融领域,专门提供各类数学计算公式的表达式引擎。 如Express4j。表达式引擎和脚本引擎比起来更轻量化,更强调性能。
表达式引擎的应用场景如定义一个风险规则表达式,当执行业务逻辑前对输入数据是进行检查。
在Spring 项目推荐使用SPEL进行表达式计算。
引擎 |
特点 |
Fel |
轻量级的、高效的表达式计算引擎。 |
OGNL |
通过简单一致的表达式语法,可以存取对象属性,调用对象的方法,遍历整个对象,实现字段类型转化等功能。 |
SpEL |
Spring表达式语言(简称“SpEL”)支持在运行时查询和操作对象。属于Spring技术体系,可以很好融合与Spring项目,比如操作Bean对象等,应用于注解、Bean定义等。 https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions |
Expression4j |
Expression4js使用JAVA实现;是专门针对金融公司的指标数据计算的一个量化跟踪引擎项目中进行抽象而来的一个子项目。可以直接支持数学公式的计算。 https://expression4j.sourceforge.net/ |
Aviator |
Aviator是一个高性能、轻量级的java语言实现的表达式求值引擎,主要用于各种表达式的动静求值。 http://fnil.net/aviator/ |
QLExpress |
为阿里的电商业务规则、表达式(布尔组合)、特殊数学公式计算(高精度)、语法分析、脚本二次定制等强需求而设计的一门动态脚本引擎解析工具。具有线程安全、高效、弱类型、安全控制等特点 https://github.com/alibaba/QLExpress |
MVEL |
MVEL为 MVFLEX Expression Language(MVFLEX表达式语言)的缩写,它是一种动态/静态的可嵌入的表达式语言和为Java平台提供Runtime执行功能。 https://github.com/mvel/mvel |
Fel 样例:
FelEngine fel = new FelEngineImpl();
Object result = fel.eval(“3000*10+7500”);
System.out.println(result);
Ognl样例:
Ognl.getValue("#user.name", context, user)
Express4j 样例:
Expression expression = ExpressionFactory.createExpression("f()=abs(-3)");
expression.evaluate(null).getRealValue();
SPEL 样例:
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext standardEvaluationContext = new StandardEvaluationContext();
standardEvaluationContext.setBeanResolver(new BeanFactoryResolver(applicationContext));
parser.parseExpression("@testService.test()").getValue(standardEvaluationContext, Boolean.class);
脚本引擎提供的是脚本执行能力,表达式引擎提供的是表达式计算能力,规则引擎是供以规则为核心的编程模型。规则引擎通常支持多种表达式语言引擎作为规则条件的执行引擎。
脚本引擎、表达式引擎是工具,规则引擎是框架或者甚至是产品(Drools)。
Drools是一种业务规则管理系统(BRMS)解决方案。它提供了一个核心业务规则引擎(BRE)、一个创建和管理规则web应用程序(Drools Workbench)、对Decision Model and Notation (DMN)提供了一致性级别中Level3的支持,同时提供了IDE的开发插件。
Drools是一个重量级的规则引擎产品,很多像金融行业、电信行业的大公司都使用它作为规则引擎。
项目官网:Drools - Drools - Business Rules Management System (Java™, Open Source)
项目地址:GitHub - kiegroup/drools: Mirror of https://github.com/apache/incubator-kie-drools
在项目中如果仅仅是使用Drools的规则引擎核心BRE,可以引入依赖Maven使用,也可以使用BRMS的整套解决方案以Drools为核心构建业务系统。
样例:
package org.drools.examples.cashflow; dialect "mvel" rule "Increase balance for AccountPeriod Credits" when // 模式匹配:到工作内存中查找特定类型对象,可以提供匹配表达式,如purchaseQuantity == 1 // 如果条件成立,$ap,一般以$开头,和fact对象区分开 ap : AccountPeriod( ) $acc : Account( ) cf : CashFlow( type == CashFlowType.CREDIT, accountNo == $acc.accountNo, date >= ap.start && <= ap.end ) then $acc.balance = $acc.balance + cf.amount; end rule "Decrease balance for AccountPeriod Debits" when ap : AccountPeriod( ) $acc : Account( ) cf : CashFlow( type == CashFlowType.DEBIT, accountNo == $acc.accountNo, date >= ap.start && <= ap.end ) then $acc.balance = $acc.balance - cf.amount; end end |
KieContainer kc = KieServices.Factory.get().getKieClasspathContainer(); KieSession ksession = kc.newKieSession( "CashFlowKS"); AccountPeriod acp = new AccountPeriod(date( "2013-01-01"), date( "2013-03-31")); Account ac = new Account(1, 0); CashFlow cf1 = new CashFlow(date( "2013-01-12"), 100, CashFlowType.CREDIT, 1 ); FactHandle fh = ksession.insert( acp ); ksession.insert( ac ); ksession.insert( cf1 ); ksession.fireAllRules(); |
EasyRules是一个轻量的规则引擎框架, 它提供了Rule抽象来创建带有条件和操作的规则,以及使用API:RulesEngine。
主要特性:
项目地址:https://github.com/j-easy/easy-rules
样例:
Rule: 当Ccondition满足,则执行Action
@Rule(name = "weather rule", description = "if it rains then take an umbrella") public class WeatherRule { @Condition public boolean itRains(@Fact("rain") boolean rain) { return rain; } @Action public void takeAnUmbrella() { System.out.println("It rains, take an umbrella!"); } } |
name: "weather rule" description: "if it rains then take an umbrella" condition: "rain == true" actions: - "System.out.println(\"It rains, take an umbrella!\");" |
// define facts Facts facts = new Facts(); facts.put("rain", true); // define rules MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader()); Rule weatherRule = ruleFactory.createRule(new FileReader("weather-rule.yml")); Rules rules = new Rules(); rules.register(weatherRule); // fire rules on known facts RulesEngine rulesEngine = new DefaultRulesEngine(); rulesEngine.fire(rules, facts); |