1. 为什么需要这样的架构设计
在构建复杂业务系统时,我们经常会遇到这样的场景:系统需要支持多种业务策略的动态切换,同时要管理大量命令处理器实例的创建与销毁,还要实现服务间的自动注册与发现。传统硬编码的方式会导致代码臃肿、难以维护,这正是我们需要策略模式+工厂模式+Spring Bean生命周期管理组合拳的原因。
我去年负责过一个电商促销系统改造项目,最初版本用if-else堆砌了二十多种促销策略,每次新增策略都要修改核心业务类,上线前测试人员总是提心吊胆。后来采用本文架构重构后,新策略开发周期从3天缩短到2小时,系统稳定性提升显著。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心模式解析与组合优势
2.1 策略模式的实战应用
策略模式的核心在于定义算法族,将每个算法封装起来并使它们可以互相替换。在实际项目中,我习惯这样定义策略接口:
java复制public interface DiscountStrategy {
String getStrategyName();
BigDecimal applyDiscount(OrderContext context);
}
关键点在于策略接口要包含策略标识方法(如getStrategyName()),这是后续自动注册的基础。我曾见过有团队直接使用类名作为标识,这在重构时会导致灾难——建议显式定义策略名称。
2.2 工厂模式的进阶实现
传统的简单工厂已经不能满足现代应用需求。我们需要的是一种能自动发现所有实现类并缓存实例的智能工厂:
java复制public class StrategyFactory {
private final Map<String, DiscountStrategy> strategyMap = new ConcurrentHashMap<>();
public void registerStrategy(String name, DiscountStrategy strategy) {
strategyMap.put(name, strategy);
}
public DiscountStrategy getStrategy(String name) {
return Optional.ofNullable(strategyMap.get(name))
.orElseThrow(() -> new IllegalArgumentException("未定义的策略: " + name));
}
}
特别注意这里的线程安全设计——使用ConcurrentHashMap避免并发问题。去年双十一大促时,我们就因为没注意这点导致策略偶发丢失,教训深刻。
2.3 Spring生命周期管理的巧妙结合
Spring的InitializingBean和DisposableBean接口常被忽视,但它们对资源管理至关重要:
java复制@Service
public class VipDiscountStrategy implements DiscountStrategy, InitializingBean {
@Autowired
private StrategyFactory factory;
@Override
public void afterPropertiesSet() {
factory.registerStrategy("VIP", this);
}
//...其他实现
}
这种设计让策略类在Spring容器初始化时自动注册到工厂,完全解耦。有个容易踩的坑:当使用@PostConstruct注解时要注意执行顺序问题,我推荐直接用InitializingBean接口更可靠。
3. 完整架构实现详解
3.1 组件关系图与数据流
(此处应有文字描述组件关系,替代图表)
整个架构包含三个核心层次:
- 策略实现层:各个具体的策略类实现业务逻辑
- 工厂管理层:负责策略的注册、存储和获取
- 命令分发层:根据上下文选择并执行策略
数据流动典型路径:
客户端请求 → 命令解析 → 工厂获取策略实例 → 策略执行 → 结果返回
3.2 自动注册的三种实现方案
方案一:基于InitializingBean(如前所示)
方案二:使用ApplicationListener监听ContextRefreshedEvent
方案三:自定义BeanPostProcessor
我团队经过压测发现,方案三的性能最好但实现复杂,方案一最适合大多数场景。这里给出方案二的示例:
java复制@Component
public class StrategyRegistry implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private StrategyFactory factory;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
Map<String, DiscountStrategy> beans = event.getApplicationContext()
.getBeansOfType(DiscountStrategy.class);
beans.forEach((name, strategy) ->
factory.registerStrategy(strategy.getStrategyName(), strategy));
}
}
3.3 命令分发器的设计要点
命令分发器需要处理的最复杂情况是策略的级联执行。这是我们线上使用的模板:
java复制public class CommandDispatcher {
private final StrategyFactory factory;
private final List<StrategyExecutionInterceptor> interceptors;
public Object dispatch(CommandRequest request) {
String strategyName = resolveStrategyName(request);
DiscountStrategy strategy = factory.getStrategy(strategyName);
// 拦截器预处理
interceptors.forEach(i -> i.preHandle(request));
try {
Object result = strategy.execute(request);
// 拦截器后处理
interceptors.forEach(i -> i.postHandle(request, result));
return result;
} catch (Exception e) {
interceptors.forEach(i -> i.onError(request, e));
throw e;
}
}
}
特别注意这里的拦截器链设计,它使得我们可以无侵入地添加日志、监控等功能。上周刚用这个特性快速实现了策略执行的Prometheus监控。
4. 生产环境中的实战经验
4.1 性能优化关键指标
在我们的支付系统中,该架构需要满足以下SLA:
- 策略查找时间 < 5ms
- 单策略执行时间 < 50ms
- 99.9%的请求延迟 < 100ms
实现要点:
- 工厂使用ConcurrentHashMap,读性能O(1)
- 策略实例保持无状态,避免同步开销
- 预热所有策略实例,避免冷启动问题
4.2 常见问题排查指南
问题现象:策略执行时报NoSuchStrategy异常
排查步骤:
- 检查工厂注册表是否包含该策略
- 确认策略类是否被Spring管理
- 查看策略名称是否匹配(注意大小写)
- 检查类路径扫描是否包含策略包
问题现象:内存持续增长
解决方案:
- 确认策略实例是否是单例
- 检查策略中是否有大对象缓存
- 使用JProfiler分析对象引用链
4.3 监控与运维建议
我们采用的监控方案:
- 每个策略打标(Micrometer)
- 记录策略执行时间分布
- 工厂注册表大小告警
- 策略执行异常率监控
关键grafana面板配置:
code复制strategy_execution_time{strategy=~"$strategy"} / 1000
5. 架构演进与扩展思路
当前架构已经支持了我们业务中90%的场景,但对于超大规模策略集(>1000种)还需要优化:
- 按业务域拆分多个工厂实例
- 实现策略的懒加载机制
- 增加策略的热更新能力
最近正在试验的方案是将策略元数据存入Redis,通过pub/sub实现动态更新。一个有趣的发现是:使用Redis的hash结构存储策略特征,可以将策略匹配时间再降低40%。
在云原生环境下,这套架构可以很容易地与Kubernetes的Operator模式结合,实现策略的CRD管理。我们正在将策略的生命周期事件与K8s的Event体系打通,这为策略的灰度发布提供了新可能。
