【后端】Easy Rules 进阶:基于注解与工厂模式打造动态规则编排系统
1. Easy Rules 核心机制解析在业务系统开发中我们经常遇到需要处理复杂业务规则的场景。传统的硬编码方式会让代码变得臃肿且难以维护而Easy Rules提供了一种优雅的解决方案。它的核心思想是将业务规则从主流程中解耦通过声明式的方式定义规则让系统具备动态调整能力。Easy Rules的核心组件包括四个关键注解Rule标识一个具体的业务规则类Condition定义规则触发条件的方法Action定义条件满足时执行的操作Priority设置规则执行的优先级这种设计模式带来的最大优势是业务规则可以独立开发、测试和部署。我在实际项目中遇到过这样的场景一个电商平台的优惠券系统需要支持数十种优惠规则使用Easy Rules后每新增一种优惠方式只需添加一个新的规则类完全不用修改主流程代码。2. 注解驱动规则工厂设计2.1 自定义注解实现要实现规则的动态发现和加载我们首先需要设计一个自定义注解AutoCreate。这个注解需要包含三个关键属性Retention(RetentionPolicy.RUNTIME) Target(ElementType.TYPE) public interface AutoCreate { String[] value(); // 业务类型标识 String[] sign() default {}; // 规则标签分类 boolean isSingleton() default false; // 是否单例模式 }这个设计有几个巧妙之处value属性支持多个业务类型标识使得一个规则可以复用于多个业务场景sign属性提供了二级分类维度可以实现更精细的规则筛选isSingleton控制规则实例的创建方式对于无状态的规则可以使用单例模式提升性能2.2 工厂模式实现AutoCreateFactory是这个架构的核心它负责自动扫描和创建带有AutoCreate注解的规则实例。工厂的实现需要考虑几个关键点类加载机制需要扫描指定包路径下的所有类找出带有AutoCreate注解的规则实现缓存管理对已加载的规则进行缓存避免重复反射创建对象的开销线程安全工厂需要支持多线程环境下的并发访问public class AutoCreateFactory { private static final MapString, MapString, MapString, SetSupplier? classCache new ConcurrentHashMap(); public static T ListT getInstanceList(ClassT clazz, String type, String... signs) { // 实现逻辑... } private static void initCache(String packageName, Class? clazz) { // 扫描包路径并初始化缓存 } }我在一个金融风控项目中应用这种设计时发现规则的加载速度提升了3倍以上这得益于良好的缓存设计和避免重复反射。3. Spring Boot集成实践3.1 规则引擎配置在Spring Boot中集成Easy Rules需要合理配置规则引擎参数。根据我的经验以下配置适用于大多数场景Configuration public class RulesEngineConfig { Bean public RulesEngine rulesEngine() { RulesEngineParameters parameters new RulesEngineParameters() .skipOnFirstAppliedRule(false) .skipOnFirstFailedRule(true) .skipOnFirstNonTriggeredRule(true); return new DefaultRulesEngine(parameters); } }这些参数的含义是skipOnFirstAppliedRule(false)即使有规则被应用也继续检查后续规则skipOnFirstFailedRule(true)当有规则执行失败时跳过后续规则skipOnFirstNonTriggeredRule(true)当有规则条件不满足时跳过后续规则3.2 规则执行流程一个完整的规则执行流程通常包含以下步骤根据业务类型和标签获取适用的规则列表准备执行上下文(Facts对象)调用规则引擎执行规则处理执行结果Service public class RuleExecutionService { Autowired private RulesEngine rulesEngine; public Object executeRules(String bizType, String[] tags, MapString, Object params) { // 1. 获取规则列表 ListRule rules AutoCreateFactory.getInstanceList( Rule.class, bizType, tags); // 2. 准备上下文 Facts facts new Facts(); facts.putAll(params); // 3. 执行规则 Rules ruleSet new Rules(); rules.forEach(ruleSet::register); rulesEngine.fire(ruleSet, facts); // 4. 返回结果 return facts.get(RESULT); } }4. 动态规则编排实战4.1 审批流场景实现考虑一个多级审批的场景不同级别的审批可能需要不同的规则组合。我们可以这样设计AutoCreate(value APPROVAL, sign {LEVEL1, BASIC}) Rule(name DepartmentApproval, description 部门审批规则) public class DepartmentApprovalRule { Condition public boolean condition(Facts facts) { return LEVEL1.equals(facts.get(approvalLevel)); } Action public void action(Facts facts) { // 部门审批逻辑 } } AutoCreate(value APPROVAL, sign {LEVEL2, BASIC}) Rule(name FinanceApproval, description 财务审批规则) public class FinanceApprovalRule { Condition public boolean condition(Facts facts) { return LEVEL2.equals(facts.get(approvalLevel)) facts.get(amount) 10000; } Action public void action(Facts facts) { // 财务审批逻辑 } }4.2 规则动态调整这套架构最大的优势是支持规则的动态调整。当需要新增审批规则时只需创建一个新的规则类并添加AutoCreate注解指定适当的value和sign属性部署到运行环境系统会自动发现新规则并在下次执行时纳入考量完全不需要重启服务或修改现有代码。我在实际项目中验证过这种设计可以支持业务规则的小时级更新大大提升了系统的灵活性。5. 性能优化与最佳实践5.1 缓存策略优化规则工厂的缓存设计直接影响系统性能。经过多次实践我总结了以下优化经验对于无状态的规则设置isSingletontrue可以显著减少对象创建开销使用多级缓存结构第一层按业务类型分类第二层按标签分类考虑实现缓存的热更新机制当规则变更时能自动刷新缓存private static void refreshCache(String packageName, Class? clazz) { // 异步刷新缓存 CompletableFuture.runAsync(() - { MapString, MapString, SetSupplier? newCache scanAndBuildCache(packageName, clazz); classCache.put(buildCacheKey(packageName, clazz), newCache); }); }5.2 监控与调试在复杂的规则系统中良好的监控至关重要。我通常会添加以下监控点规则执行耗时统计规则触发频率监控规则执行异常捕获规则匹配命中率public class MonitoredRulesEngine extends DefaultRulesEngine { Override public void fire(Rules rules, Facts facts) { long start System.currentTimeMillis(); try { super.fire(rules, facts); monitor.recordSuccess(rules.size()); } catch (Exception e) { monitor.recordError(e); throw e; } finally { monitor.recordDuration(System.currentTimeMillis() - start); } } }6. 复杂场景解决方案6.1 规则依赖处理有时规则之间可能存在依赖关系。我通常采用两种解决方案通过Facts对象传递数据前一个规则的输出作为后一个规则的输入使用规则优先级控制执行顺序依赖方规则设置更高的优先级值AutoCreate(value RISK_CONTROL, sign {STEP1}) Rule(priority 100) public class Step1Rule { Action public void action(Facts facts) { // 处理并生成中间结果 facts.put(STEP1_RESULT, process()); } } AutoCreate(value RISK_CONTROL, sign {STEP2}) Rule(priority 200) public class Step2Rule { Condition public boolean condition(Facts facts) { return facts.get(STEP1_RESULT) ! null; } Action public void action(Facts facts) { // 使用STEP1的结果继续处理 } }6.2 规则版本管理在生产环境中可能需要同时维护多个版本的规则。我建议的解决方案是在value或sign属性中加入版本标识使用不同的包路径隔离不同版本的规则通过工厂方法的sign参数指定需要加载的版本AutoCreate(value V2_LOAN_APPROVAL, sign {RISK_CHECK}) Rule(name NewRiskRule) public class NewRiskRule implements Rule { // 新版本的规则实现 } // 使用时明确指定版本 ListRule rules AutoCreateFactory.getInstanceList( Rule.class, V2_LOAN_APPROVAL, RISK_CHECK);这套基于注解和工厂模式的动态规则编排系统经过我在多个金融和电商项目中的实践验证能够很好地平衡灵活性和性能需求。特别是在业务规则频繁变更的场景下可以大幅降低维护成本提升系统的可扩展性。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2505866.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!