Spring 策略模式實現:工廠方法與自動注入詳解
1. 背景介紹
在復雜的業務系統中,我們常常需要根據不同的場景選擇不同的處理策略。本文將詳細介紹在 Spring 框架中實現策略模式的兩種主要方法。
2. 方案一: 手動注冊工廠模式
2.1 定義工廠類
@Component
public class CalculateHandlerFactory implements InitializingBean, ApplicationContextAware {private ApplicationContext applicationContext;// 存儲不同策略的映射private static final Map<String, CalculateHandler> HANDLER_MAP = new HashMap<>();// 初始化方法,自動注冊所有處理器@Overridepublic void afterPropertiesSet() throws Exception {applicationContext.getBeansOfType(CalculateHandler.class).values().forEach(v -> HANDLER_MAP.putIfAbsent(v.getType(), v));}// 設置 ApplicationContext@Overridepublic 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 定義策略接口
public interface CalculateHandler {// 獲取處理類型String getType();// 具體計算方法double calculate(double a, double b);
}
3.2 實現具體策略
@Component
public class AddCalculateHandler implements CalculateHandler {@Overridepublic String getType() {return "add";}@Overridepublic double calculate(double a, double b) {return a + b;}
}@Component
public class SubtractCalculateHandler implements CalculateHandler {@Overridepublic String getType() {return "subtract";}@Overridepublic double calculate(double a, double b) {return a - b;}
}
3.3 策略管理服務
@Service
public class CalculateService {@Autowiredprivate 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 按優先級排序
@Service
public class CalculateService {@Autowiredprivate Map<String, CalculateHandler> calculateHandlerMap;// 按照 @Order 注解排序@Autowiredprivate List<CalculateHandler> calculateHandlerList;
}
4.2 自定義 Key 獲取
@Service
public class CalculateService {@Autowiredprivate Map<String, CalculateHandler> calculateHandlerMap;// 使用自定義方法獲取 Keypublic 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 的依賴注入和自動裝配機制,我們可以非常優雅地實現策略模式,使代碼更加靈活、可讀和可維護。選擇合適的實現方式,需要根據具體的業務場景和系統架構來權衡。