Spring 策略模式实现:工厂方法与自动注入详解
1. 背景介绍
在复杂的业务系统中,我们常常需要根据不同的场景选择不同的处理策略。本文将详细介绍在 Spring 框架中实现策略模式的两种主要方法。
2. 方案一: 手动注册工厂模式
2.1 定义工厂类
java
@Component
public class CalculateHandlerFactory implements InitializingBean, ApplicationContextAware {
private ApplicationContext applicationContext;
// 存储不同策略的映射
private static final Map<String, CalculateHandler> HANDLER_MAP = new HashMap<>();
// 初始化方法,自动注册所有处理器
@Override
public void afterPropertiesSet() throws Exception {
applicationContext.getBeansOfType(CalculateHandler.class)
.values()
.forEach(v -> HANDLER_MAP.putIfAbsent(v.getType(), v));
}
// 设置 ApplicationContext
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
// 根据类型获取对应的处理器
public CalculateHandler getCalculateHandler(String calculateType) {
return HANDLER_MAP.getOrDefault(calculateType, null);
}
}
2.2 实现步骤
- 实现
InitializingBean
接口,在 Bean 初始化时自动注册处理器 - 实现
ApplicationContextAware
接口,获取 Spring 应用上下文 - 使用
Map
存储不同类型的处理器 - 提供根据类型获取处理器的方法
2.3 优点
- 对注册过程有精细化控制
- 可以添加自定义的注册逻辑
- 灵活性高
3. 方案二: 自动注入策略模式
3.1 定义策略接口
java
public interface CalculateHandler {
// 获取处理类型
String getType();
// 具体计算方法
double calculate(double a, double b);
}
3.2 实现具体策略
java
@Component
public class AddCalculateHandler implements CalculateHandler {
@Override
public String getType() {
return "add";
}
@Override
public double calculate(double a, double b) {
return a + b;
}
}
@Component
public class SubtractCalculateHandler implements CalculateHandler {
@Override
public String getType() {
return "subtract";
}
@Override
public double calculate(double a, double b) {
return a - b;
}
}
3.3 策略管理服务
java
@Service
public class CalculateService {
@Autowired
private Map<String, CalculateHandler> calculateHandlerMap;
public double calculate(String type, double a, double b) {
CalculateHandler handler = calculateHandlerMap.get(type);
if (handler == null) {
throw new IllegalArgumentException("未找到对应的计算处理器");
}
return handler.calculate(a, b);
}
}
3.4 工作原理详解
3.4.1 自动注册机制
当使用 @Autowired Map<String, Interface>
时,Spring 会:
- 扫描所有实现指定接口的 Bean
- 使用 Bean 名称作为 Map 的 Key
- 使用 Bean 实例作为 Map 的 Value
3.4.2 Bean 名称规则
- 默认使用类名首字母小写作为 Bean 名称
- 可以通过
@Component("customName")
自定义 Bean 名称
4. 高级用法
4.1 按优先级排序
java
@Service
public class CalculateService {
@Autowired
private Map<String, CalculateHandler> calculateHandlerMap;
// 按照 @Order 注解排序
@Autowired
private List<CalculateHandler> calculateHandlerList;
}
4.2 自定义 Key 获取
java
@Service
public class CalculateService {
@Autowired
private Map<String, CalculateHandler> calculateHandlerMap;
// 使用自定义方法获取 Key
public double calculate(String type, double a, double b) {
CalculateHandler handler = calculateHandlerMap.values().stream()
.filter(h -> h.getType().equals(type))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("未找到处理器"));
return handler.calculate(a, b);
}
}
5. 两种方案对比
特性 | 手动注册 | 自动注入 |
---|---|---|
代码复杂度 | 较高 | 较低 |
灵活性 | 高 | 低 |
初始化控制 | 精细 | 简单 |
性能 | 略低 | 略高 |
6. 优点和适用场景
6.1 优点
- 代码解耦
- 动态扩展
- 无需手动维护注册表
- 充分利用 Spring 依赖注入机制
6.2 适用场景
- 策略模式
- 插件化开发
- 可插拔的业务处理器
- 系统扩展性要求高的场景
7. 注意事项
- 保证 Bean 名称唯一性
- 接口设计要合理
- 做好异常处理
- 考虑性能和扩展性
8. 最佳实践
- 保持接口简洁明了
- 明确定义每个策略的职责
- 合理设计方法签名
- 添加必要的异常处理
- 考虑性能和扩展性
结论
通过 Spring 的依赖注入和自动装配机制,我们可以非常优雅地实现策略模式,使代码更加灵活、可读和可维护。选择合适的实现方式,需要根据具体的业务场景和系统架构来权衡。