Spring 框架大量使用模板方法模式與回調模式的組合,這是其消除樣板代碼、提供擴展點、保持框架靈活性的核心架構秘訣。JdbcTemplate、RestTemplate、RedisTemplate、TransactionTemplate 皆是典型代表。
抽象父類 (Template) ├── templateMethod() final // 定義算法骨架,不可覆寫 │ ├── step1() protected abstract // 子類必須實現 │ ├── step2() protected hook // 子類可選覆寫 │ └── step3() protected abstract // 子類必須實現 └── 具體子類 1, 2, 3... // 實現各步驟邏輯
HttpServlet 定義 service() 模板方法doGet()/doPost() 等鉤子方法| 問題 | 影響 |
|---|---|
| 單繼承限制 | 子類無法繼承業務基類,被框架類佔用繼承名額 |
| 類爆炸 | 每個業務變體需新建子類,JdbcTemplate 幾十個方法將導致大量匿名內部類 |
| 靜態綁定 | 編譯期確定行為,運行時難以動態切換策略 |
| 受保護方法洩露 | protected 方法暴露內部細節,破壞封裝 |
將可變行為封裝為接口,作為參數傳遞給模板方法,實現運行時行為注入。
// JdbcTemplate 核心回調接口
public interface RowMapper {
T mapRow(ResultSet rs, int rowNum) throws SQLException;
}
public interface ResultSetExtractor {
T extractData(ResultSet rs) throws SQLException, DataAccessException;
}
public interface PreparedStatementCreator {
PreparedStatement createPreparedStatement(Connection con) throws SQLException;
}
public interface ConnectionCallback {
T doInConnection(Connection con) throws SQLException, DataAccessException;
}
// 模板方法:處理資源獲取、異常轉換、資源釋放等固定流程
public T query(String sql, RowMapper rowMapper) {
return execute(new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection con) {
return con.prepareStatement(sql);
}
}, new ResultSetExtractor() {
public T extractData(ResultSet rs) {
List results = new ArrayList<>();
int rowNum = 0;
while (rs.next()) {
results.add(rowMapper.mapRow(rs, rowNum++)); // 回調:行映射邏輯
}
return results;
}
});
}
// 使用者代碼:僅關注業務邏輯
List users = jdbcTemplate.query(
"SELECT id, name, email FROM users WHERE status = ?",
new Object[]{1},
(rs, rowNum) -> new User(rs.getLong("id"), rs.getString("name"), rs.getString("email"))
);
| 場景特徵 | 推薦模式 | 理由 |
|---|---|---|
| 每個步驟都有實質性不同邏輯需實現 | 純模板方法 | 回調接口過多,反而增加複雜度,不如直接繼承覆寫 |
| 僅需定制 1-2 關鍵步驟,其餘固定 | 模板+回調 | 接口少、Lambda 簡潔、靈活組合 |
| 步驟定制僅涉及參數/配置變化 | 模板+回調/策略模式 | 可傳遞配置對象,避免創建大量接口實現 |
| 需運行時動態切換行為 | 模板+回調 | 組合模式天然支持運行時替換 |
| 框架核心流程極其穩定,擴展點固定 | 模板方法 | 編譯期約束更強,IDE 支持更好 |
// Java 8+ Lambda 极大簡化回調寫法
jdbcTemplate.query(sql, (rs, rowNum) -> new User(...));
// Spring 5+ 函數式 API
Flux users = client.get()
.uri("/users")
.retrieve()
.bodyToFlux(User.class); // 響應式模板
// TransactionTemplate 現代用法
transactionTemplate.execute(status -> {
// 業務邏輯
return orderService.createOrder(order);
});
Spring 的 Template + Callback 組合是框架設計的教科書級範例:用繼承固定骨架,用組合注入變化。這不僅解決了樣板代碼問題,更通過接口隔離、依賴倒置、單一職責等 SOLID 原則,構建了高內聚、低耦合、易測試、易擴展的企業級框架架構。理解這一模式組合,是掌握 Spring 源碼、設計優雅 API 的關鍵鑰匙。