Java中StringBuilder生成相同字符串不相等及Drools包差异审计需求
Q2: Drools包函数差异审计的技术实现方案
我之前做规则引擎运维平台时刚好做过类似功能,核心思路是先解析提取新旧包的函数元数据,再通过唯一标识比对实现增删改判定,具体拆成这几个步骤:
1. 定义函数元数据模型
先把Drools函数的关键信息封装成实体,用来存储比对所需的核心数据:
public class DroolsFunctionMeta { // 函数唯一标识:函数名+参数类型签名(比如"calculateTotal(java.lang.String,int)") private String uniqueId; private String functionName; // 参数类型列表,比如["java.lang.String", "int"] private List<String> paramTypes; private String returnType; // 标准化后的函数体文本(避免换行空格导致误判) private String functionBody; // 可选:函数所在DRL文件路径、注释等 private String drlFilePath; }
这里的uniqueId是核心——Drools支持函数重载,必须用「函数名+参数类型签名」作为唯一标识,才能区分同名不同参数的函数。
2. 解析Drools包提取函数元数据
推荐优先用Drools官方API,比正则更准确:
方式一:利用Drools Kie API解析
通过KieServices加载DRL内容,从规则包描述符中提取函数信息:
KieServices kieServices = KieServices.Factory.get(); KieFileSystem kfs = kieServices.newKieFileSystem(); // 把DRL内容写入KieFileSystem(从数据库或文本框获取) kfs.write("src/main/resources/rules/myPackage.drl", kieServices.getResources().newByteArrayResource(drlContent.getBytes())); KieBuilder kieBuilder = kieServices.newKieBuilder(kfs).buildAll(); // 获取KieBase并提取函数描述符 KieBase kieBase = kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId()).getKieBase(); Collection<FunctionDescr> functionDescrs = kieBase.getKiePackage("myPackage").getFunctions(); // 把FunctionDescr转成自定义的DroolsFunctionMeta for (FunctionDescr descr : functionDescrs) { DroolsFunctionMeta meta = new DroolsFunctionMeta(); meta.setFunctionName(descr.getName()); meta.setReturnType(descr.getReturnType()); // 提取参数类型 List<String> paramTypes = descr.getParameters().stream() .map(ParamDescr::getType) .collect(Collectors.toList()); meta.setParamTypes(paramTypes); // 生成唯一ID meta.setUniqueId(descr.getName() + "(" + String.join(",", paramTypes) + ")"); // 从原始DRL中匹配函数体(FunctionDescr不直接存函数体文本) // 可结合正则从drlContent中提取对应函数的代码块 }
方式二:正则表达式解析(轻量场景)
如果不想依赖Kie API,先去除DRL中的单行/多行注释,再用正则匹配函数定义:
// 匹配函数定义的正则,支持多行 function\s+([a-zA-Z0-9_]+)\s+([a-zA-Z0-9_]+)\s*\(([^)]*)\)\s*\{([\s\S]*?)\}
注意:正则要处理函数体嵌套大括号的边界情况,复杂场景建议用ANTLR生成Drools语法解析器来提取。
3. 新旧函数集的差异比对逻辑
用Map做索引实现快速比对:
// 1. 旧函数集转成Map,key为uniqueId Map<String, DroolsFunctionMeta> oldFunctionMap = oldFunctionList.stream() .collect(Collectors.toMap(DroolsFunctionMeta::getUniqueId, meta -> meta)); // 2. 遍历新函数集,判断新增/修改 List<FunctionChangeRecord> changeRecords = new ArrayList<>(); for (DroolsFunctionMeta newMeta : newFunctionList) { String uniqueId = newMeta.getUniqueId(); if (!oldFunctionMap.containsKey(uniqueId)) { // 新增函数 changeRecords.add(new FunctionChangeRecord(newMeta, ChangeType.ADD)); } else { DroolsFunctionMeta oldMeta = oldFunctionMap.get(uniqueId); // 比对核心字段,标准化文本避免格式差异误判 boolean isModified = !normalizeText(newMeta.getFunctionBody()).equals(normalizeText(oldMeta.getFunctionBody())) || !newMeta.getReturnType().equals(oldMeta.getReturnType()); if (isModified) { // 修改函数,记录新旧差异 changeRecords.add(new FunctionChangeRecord(oldMeta, newMeta, ChangeType.MODIFY)); } // 移除已匹配的旧函数,剩余的就是被删除的 oldFunctionMap.remove(uniqueId); } } // 3. 旧函数Map中剩余的就是删除的函数 for (DroolsFunctionMeta deletedMeta : oldFunctionMap.values()) { changeRecords.add(new FunctionChangeRecord(deletedMeta, ChangeType.DELETE)); }
其中normalizeText()用来标准化文本,去除多余换行、空格:
private static String normalizeText(String text) { return text.replaceAll("\\s+", " ").trim(); }
4. 差异结果的存储与展示
把changeRecords存储到数据库审计表,或生成可视化报告:
- 对修改的函数,用
diff-match-patch等工具生成函数体的差异高亮; - 前端用表格展示所有变更,点击修改项可展开对比详情。
内容的提问来源于stack exchange,提问作者SagittariusA
相关产品推荐
相关产品推荐

