1. 项目背景与问题痛点
在开发企业级架构巡检系统时,我们遇到了一个典型的业务场景:系统需要支持数十种不同的架构巡检规则,每种规则都有独立的执行逻辑。最初我们采用传统的if-else分支处理方式,但随着规则数量增加,代码迅速变得难以维护。
以API依赖检测为例,系统需要处理:
- API依赖过多检测(api_fan_in)
- API循环依赖检测(api_cycle)
- 跨层调用检测(api_layer)
- 微服务循环依赖检测(ms_cycle)
关键问题:每新增一个规则类型,就需要在RuleEngine中新增一个if分支,这直接违反了开闭原则(OCP)——对扩展开放,对修改关闭。
2. 传统实现方式分析
2.1 典型if-else实现
java复制@Service
public class RuleEngine {
@Autowired private ApiFanInRuleExecutor fanInRuleExecutor;
@Autowired private ApiCycleRuleExecutor cycleRuleExecutor;
public void executeRule(Inspection inspection, Rule rule) {
if ("api_fan_in".equals(rule.getRuleType())) {
fanInRuleExecutor.execute(inspection, rule);
} else if ("api_cycle".equals(rule.getRuleType())) {
cycleRuleExecutor.execute(inspection, rule);
}
// 更多else if...
}
}
2.2 存在的问题
- 维护成本高:每新增规则都需要修改RuleEngine类
- 可读性差:当规则超过10个时,代码会变得冗长难读
- 测试困难:任何规则改动都需要重新测试整个RuleEngine
- 违反SOLID原则:
- 单一职责原则(SRP):RuleEngine承担过多职责
- 开闭原则(OCP):扩展需要修改已有代码
3. 解决方案设计
3.1 策略模式核心思想
通过定义统一的规则执行接口,将每种规则的具体实现解耦:
java复制public interface RuleExecutor {
String getRuleType(); // 返回规则类型标识
void execute(Inspection inspection, Rule rule);
}
3.2 Spring自动装配机制
利用Spring的依赖注入特性:
- 所有实现RuleExecutor的类添加
@Component注解 - 通过
@Autowired List<RuleExecutor>获取所有实现 - 初始化时构建规则类型与执行器的映射关系
3.3 执行流程优化

4. 具体实现步骤
4.1 定义规则执行接口
java复制public interface RuleExecutor {
/**
* @return 规则类型标识,需全局唯一
*/
String getRuleType();
/**
* @param inspection 巡检上下文
* @param rule 规则配置
*/
void execute(Inspection inspection, Rule rule);
}
4.2 实现具体规则
以API依赖过多检测为例:
java复制@Component
public class ApiFanInRuleExecutor implements RuleExecutor {
private static final int DEFAULT_THRESHOLD = 10;
@Override
public String getRuleType() {
return "api_fan_in";
}
@Override
public void execute(Inspection inspection, Rule rule) {
int threshold = rule.getThreshold() != null ?
rule.getThreshold() : DEFAULT_THRESHOLD;
// 实际业务逻辑
if (inspection.getDependencyCount() > threshold) {
inspection.addViolation(
new Violation(rule, "API依赖数超过阈值"));
}
}
}
4.3 规则引擎核心实现
java复制@Service
public class RuleEngine {
private final Map<String, RuleExecutor> executorMap;
@Autowired
public RuleEngine(List<RuleExecutor> executors) {
this.executorMap = executors.stream()
.collect(Collectors.toMap(
RuleExecutor::getRuleType,
Function.identity()
));
}
public void execute(Inspection inspection, Rule rule) {
RuleExecutor executor = executorMap.get(rule.getType());
if (executor == null) {
throw new RuleNotFoundException(rule.getType());
}
executor.execute(inspection, rule);
}
}
5. 高级优化技巧
5.1 延迟初始化优化
对于不常用的规则,可以采用懒加载策略:
java复制@Service
public class LazyRuleEngine {
@Autowired
private ApplicationContext context;
private final Map<String, Class<? extends RuleExecutor>> executorTypes =
new ConcurrentHashMap<>();
public void registerRule(String type, Class<? extends RuleExecutor> clazz) {
executorTypes.put(type, clazz);
}
public void execute(Inspection inspection, Rule rule) {
Class<? extends RuleExecutor> clazz = executorTypes.get(rule.getType());
if (clazz == null) {
throw new RuleNotFoundException(rule.getType());
}
RuleExecutor executor = context.getBean(clazz);
executor.execute(inspection, rule);
}
}
5.2 规则优先级处理
当需要处理规则优先级时:
java复制public class PriorityRuleEngine {
private final Map<String, List<RuleExecutor>> executorMap;
@Autowired
public PriorityRuleEngine(List<RuleExecutor> executors) {
this.executorMap = executors.stream()
.collect(Collectors.groupingBy(
RuleExecutor::getRuleType,
Collectors.toList()
));
executorMap.forEach((type, list) ->
list.sort(Comparator.comparingInt(RuleExecutor::getPriority)));
}
}
6. 生产环境注意事项
6.1 线程安全问题
- 如果RuleExecutor包含状态,需要确保线程安全
- 建议实现为无状态的Bean
6.2 性能考量
- 缓存热点规则:对高频访问的规则实例进行缓存
- 预加载检查:应用启动时验证所有规则是否已注册
- 并行执行:对无依赖关系的规则采用并行执行
java复制public void executeParallel(Collection<Rule> rules) {
rules.parallelStream()
.forEach(rule -> {
RuleExecutor executor = executorMap.get(rule.getType());
executor.execute(currentInspection, rule);
});
}
6.3 异常处理策略
建议定义统一的异常处理机制:
java复制public interface RuleExecutor {
default void executeSafely(Inspection inspection, Rule rule) {
try {
execute(inspection, rule);
} catch (Exception e) {
inspection.addError(new RuleExecutionError(rule, e));
}
}
}
7. 扩展应用场景
这种模式不仅适用于规则引擎,还可应用于:
- 支付网关:不同支付渠道的处理
- 消息通知:多种通知方式(短信、邮件、站内信)
- 数据分析:多种分析算法的动态选择
- 工作流引擎:不同节点类型的处理
8. 与其他模式的结合
8.1 结合工厂模式
java复制public class RuleExecutorFactory {
private final Map<String, Supplier<RuleExecutor>> suppliers;
public RuleExecutor register(String type, Supplier<RuleExecutor> supplier) {
suppliers.put(type, supplier);
}
public RuleExecutor create(String type) {
Supplier<RuleExecutor> supplier = suppliers.get(type);
if (supplier == null) throw new IllegalArgumentException();
return supplier.get();
}
}
8.2 结合责任链模式
java复制public class ChainRuleExecutor implements RuleExecutor {
private final List<RuleExecutor> chain;
@Override
public void execute(Inspection inspection, Rule rule) {
for (RuleExecutor executor : chain) {
if (executor.canExecute(rule)) {
executor.execute(inspection, rule);
break;
}
}
}
}
9. 测试策略
9.1 单元测试示例
java复制@SpringBootTest
class RuleEngineTest {
@MockBean
private ApiFanInRuleExecutor fanInExecutor;
@Autowired
private RuleEngine ruleEngine;
@Test
void shouldExecuteCorrectRule() {
when(fanInExecutor.getRuleType()).thenReturn("api_fan_in");
Rule rule = new Rule("api_fan_in");
Inspection inspection = new Inspection();
ruleEngine.execute(inspection, rule);
verify(fanInExecutor).execute(inspection, rule);
}
}
9.2 集成测试要点
- 验证所有规则是否被正确注册
- 测试未知规则类型的处理
- 验证规则执行顺序(如有优先级)
- 测试并发执行场景
10. 性能对比数据
在规则数量增加到50个时:
| 方案 | 内存占用 | 执行耗时 | 代码行数 |
|---|---|---|---|
| if-else | 较低 | 较快 | 500+ |
| 策略模式+Map | 较高 | 最快 | 300 |
| 反射实现 | 高 | 最慢 | 200 |
实际测试表明:当规则超过10个时,策略模式+Map的实现方式在可维护性和执行效率上达到最佳平衡
11. 常见问题排查
11.1 规则未生效
- 检查
@Component注解是否遗漏 - 确认
getRuleType()返回值与规则配置一致 - 查看Spring容器是否成功加载该Bean
11.2 循环依赖问题
如果RuleExecutor之间需要相互调用:
java复制@Lazy
@Component
public class RuleA implements RuleExecutor {
@Autowired
private RuleB ruleB;
}
11.3 性能瓶颈
使用Arthas进行诊断:
bash复制watch com.example.RuleEngine execute '{params, returnObj}' -x 3
12. 最佳实践建议
- 命名规范:保持规则类型标识简洁且唯一
- 文档维护:建立规则类型与实现的映射文档
- 默认实现:提供AbstractRuleExecutor基类
- 监控指标:记录各规则执行时间和次数
java复制public abstract class AbstractRuleExecutor implements RuleExecutor {
@Override
public void execute(Inspection inspection, Rule rule) {
long start = System.currentTimeMillis();
try {
doExecute(inspection, rule);
recordSuccess(rule);
} catch (Exception e) {
recordFailure(rule, e);
throw e;
} finally {
recordTime(System.currentTimeMillis() - start);
}
}
protected abstract void doExecute(Inspection inspection, Rule rule);
}
13. 架构演进方向
当系统规模进一步扩大时:
- 动态规则加载:通过热部署添加新规则
- 规则版本管理:支持多版本规则共存
- 可视化编排:通过界面配置规则执行流程
- 分布式执行:将规则分发到不同节点执行
14. 替代方案比较
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| if-else | 简单直接 | 难以维护 | 规则数量<5 |
| 策略模式+Map | 易扩展 | 需预先加载 | 大多数场景 |
| 反射机制 | 灵活 | 性能差 | 动态类加载 |
| 规则引擎(Drools) | 功能强大 | 学习成本高 | 复杂业务规则 |
15. 个人实践心得
在实际项目中应用这种模式时,有几个关键点值得注意:
- 规则标识设计:建议采用枚举或常量类管理规则类型,避免硬编码字符串
- 依赖管理:如果规则执行器需要依赖其他服务,确保依赖关系清晰
- 生命周期控制:对于需要初始化的规则,实现SmartLifecycle接口
- 调试技巧:在开发阶段可以添加
@Order注解控制规则加载顺序
一个特别有用的调试技巧是在RuleEngine初始化时打印所有注册的规则:
java复制@PostConstruct
public void init() {
log.info("Registered rules: {}",
executorMap.keySet().stream()
.sorted()
.collect(Collectors.joining(", ")));
}