有这样的需求,我有一个map,里面放了一些key-value,自定义了一些规则,如age==24&&name==aom||phone==123456789,希望能有个引擎能判断出这个Map里的值,是否匹配上这个规则,规则里有一些简单的运算,如==、contains等。
规则是动态可变的,这样就可以灵活控制命中了规则的数据能进行一些采集。
我做了一个这样简单的工具,目前可以支持
代码语言:javascript复制//规则描述,支持的有:
//==,如 age==25,name==jerry,字符串也不要加引号
//!=,和==一样的用法
//&&、||,如age==24&&name==aom||phone==123456789,不要加括号,自行调整好顺序
//contains,如address contains(example),字符串不要加引号
//matches,如phone matches(d ),正则表达式
//in,是否在一个集合里,如age in [12,1,25],集合需要用[]包围,字符串不要引号
//isEmpty,是否为空,如address isEmpty
//isNotEmpty,同isEmpty
//!(),取非操作,如 !(address isNotEmpty),注意,!必须在最前面,要取非的,需要用小括号包围
这些常用的一些基本规则。
类似于的表达式还有spring的SpEL、mvel这些表达式引擎,但我的场景对性能的要求相当苛刻,规则倒是很简单,是无法接受这些动态规则引擎的高达十几甚至20ms的耗时。对这一个规则匹配的耗时要求不能超过1ms,所以就自己做了一个。
下面是代码
代码语言:javascript复制import java.util.Map;
/**
* 规则校验器
* @author wuweifeng wrote on 2023/3/7
* @version 1.0
*/
public class RuleValidator {
public static boolean validateRule(String rule, Map<String, String> variables) {
if (rule == null || "".equals(rule)) {
return false;
}
// 去除所有空格
rule = rule.replaceAll("\s ", "");
//判断表达式是不是取非语句,如" !(a==345) "
if (rule.startsWith("!(") && rule.endsWith(")")) {
return !validateRule(rule.substring(2, rule.length() - 1), variables);
}
// 处理 || 运算符
if (rule.contains("||")) {
int index = rule.indexOf("||");
String left = rule.substring(0, index);
String right = rule.substring(index 2);
return validateRule(left, variables) || validateRule(right, variables);
}
// 处理 && 运算符
if (rule.contains("&&")) {
int index = rule.indexOf("&&");
String left = rule.substring(0, index);
String right = rule.substring(index 2);
return validateRule(left, variables) && validateRule(right, variables);
}
// 处理括号运算符
if (rule.startsWith("(") && rule.endsWith(")")) {
String subrule = rule.substring(1, rule.length() - 1);
return validateRule(subrule, variables);
}
// 处理 == 运算符和 != 运算符
if (rule.contains("==")) {
int index = rule.indexOf("==");
String left = rule.substring(0, index);
String right = rule.substring(index 2);
if (variables.containsKey(left)) {
return variables.get(left).equals(right);
} else {
return left.equals(right);
}
} else if (rule.contains("!=")) {
int index = rule.indexOf("!=");
String left = rule.substring(0, index);
String right = rule.substring(index 2);
if (variables.containsKey(left)) {
return !variables.get(left).equals(right);
} else {
return !left.equals(right);
}
}
// 处理 contains 运算符和 in 运算符
if (rule.contains("contains")) {
int index = rule.indexOf("contains");
String left = rule.substring(0, index).replace(".", "");
String value = variables.get(left);
String right = rule.substring(index 8).replace("(", "").replace(")", "");
if (variables.containsKey(left)) {
return value.contains(right);
} else {
return false;
}
} else if (rule.contains("in")) {
int index = rule.indexOf("in");
String left = rule.substring(0, index);
String value = variables.get(left);
String right = rule.substring(index 2).replace("[", "").replace("]", "");
if (variables.containsKey(right)) {
String[] items = variables.get(right).split(",");
for (String item : items) {
if (item.trim().equals(value)) {
return true;
}
}
return false;
} else {
String[] items = right.split(",");
for (String item : items) {
if (item.trim().equals(value)) {
return true;
}
}
return false;
}
}
// 处理 isEmpty 运算符
if (rule.contains("isEmpty")) {
int index = rule.indexOf("isEmpty");
String left = rule.substring(0, index);
if (variables.containsKey(left)) {
return variables.get(left) == null || "".equals(variables.get(left));
} else {
return false;
}
}
// 处理 isEmpty 运算符
if (rule.contains("isNotEmpty")) {
int index = rule.indexOf("isNotEmpty");
String left = rule.substring(0, index);
if (variables.containsKey(left)) {
return variables.get(left) != null && !"".equals(variables.get(left));
} else {
return false;
}
}
// 处理 matches 运算符
if (rule.contains("matches")) {
int index = rule.indexOf("matches");
String left = rule.substring(0, index);
String right = rule.substring(index 7);
if (variables.containsKey(left)) {
return variables.get(left).matches(right);
} else {
return left.matches(right);
}
}
// 如果规则字符串为变量名,则直接查找Map中对应的值
if (variables.containsKey(rule)) {
return true;
}
return false;
}
}
代码语言:javascript复制import java.util.HashMap;
import java.util.Map;
/**
* @author wuweifeng wrote on 2023/3/7
* @version 1.0
*/
public class RuleValidatorTest {
//规则描述,支持的有:
//==,如 age==25,name==jerry,字符串也不要加引号
//!=,和==一样的用法
//&&、||,如age==24&&name==aom||phone==123456789,不要加括号,自行调整好顺序
//contains,如address contains(example),字符串不要加引号
//matches,如phone matches(d ),正则表达式
//in,是否在一个集合里,如age in [12,1,25],集合需要用[]包围,字符串不要引号
//isEmpty,是否为空,如address isEmpty
//isNotEmpty,同isEmpty
//!(),取非操作,如 !(address isNotEmpty),注意,!必须在最前面,要取非的,需要用小括号包围
public static void main(String[] args) {
// 初始化变量Map
Map<String, String> variables = new HashMap<>();
variables.put("name", "Tom");
variables.put("age", "25");
variables.put("email", "tom@example.com");
variables.put("phone", "123456789");
variables.put("address", "");
// 测试用例
String rule1 = "age==25";
String rule2 = "age!=25";
String rule3 = "age==30";
String rule4 = "age!=30";
String rule5 = "age==24&&name==aom||phone==123456789";
String rule6 = "age==25||name==Jerry";
String rule7 = "address contains(example)";
//正则表达式
String rule8 = "phone matches(\d )";
String rule9 = "age in [12,1,25]";
String rule10 = "age in [12,1,25] && name==Tom";
String rule11 = "address isEmpty";
String rule12 = "address isNotEmpty";
String rule13 = "!(address isNotEmpty)";
// 断言
System.out.println("rule1-" RuleValidator.validateRule(rule1, variables));
System.out.println("rule2-" RuleValidator.validateRule(rule2, variables));
System.out.println("rule3-" RuleValidator.validateRule(rule3, variables));
System.out.println("rule4-" RuleValidator.validateRule(rule4, variables));
System.out.println("------");
System.out.println("rule5-" RuleValidator.validateRule(rule5, variables));
System.out.println("rule6-" RuleValidator.validateRule(rule6, variables));
System.out.println("rule7-" RuleValidator.validateRule(rule7, variables));
System.out.println("------");
System.out.println("rule8-" RuleValidator.validateRule(rule8, variables));
System.out.println("------");
System.out.println("rule9-" RuleValidator.validateRule(rule9, variables));
System.out.println("rule10-" RuleValidator.validateRule(rule10, variables));
System.out.println("rule11-" RuleValidator.validateRule(rule11, variables));
System.out.println("rule12-" RuleValidator.validateRule(rule12, variables));
System.out.println("rule13-" RuleValidator.validateRule(rule13, variables));
}
}
整体比较简单,不支持复杂的(),最好是平铺的一些规则。