首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 模板方法模式與回調機制深度解析:從原理到 Spring JdbcTemplate 實戰

     編輯:Java 相關知識

核心概念辨析

回調函數

客戶代碼 C 調用服務代碼 S 的方法 A,S 在某個時機反向調用 C 提供的方法 B。B 即回調函數。三要素:

  1. 屬於某個執行流程的一部分。
  2. 必須遵循流程規定的調用約定(函數簽名)。
  3. 調用時機由流程控制,C 不能主動調用 B 完成流程邏輯。

Java 中通過接口或函數式接口實現回調。

模板方法模式

在抽象類中定義算法骨架(模板方法),將可變步驟延遲到子類實現(鉤子方法/抽象方法)。優勢:

  • 一次性實現不變邏輯,避免子類重複代碼。
  • 集中控制流程,子類只關注差異點。
  • 可通過 final 修飾模板方法防止被覆寫,保護算法結構。

經典實現:抽象類 + 繼承

public abstract class AbstractTemplate {
    // 模板方法:final 鎖定流程
    public final void execute() {
        step1();
        step2(); // 可變步驟
        step3();
    }
    private void step1() { System.out.println("通用前置:初始化資源"); }
    protected abstract void step2(); // 子類必須實現
    private void step3() { System.out.println("通用後置:釋放資源"); }
}

public class ConcreteImpl extends AbstractTemplate {
    @Override protected void step2() { System.out.println("具體業務邏輯"); }
}

痛點:繼承耦合度高

若抽象類有 10 個抽象方法,子類必須全部實現,即使只關心其中一個。且單繼承限制導致無法復用其他邏輯。

進化:模板方法 + 回調 (策略模式融合)

將可變步驟提取為獨立接口,作為方法參數傳入,模板類不再需要被繼承。

回調接口定義

@FunctionalInterface // Java 8+ 函數式接口,支持 Lambda
public interface StatementCallback<T> {
    T doInStatement(Statement stmt) throws SQLException;
}

模板類 (無狀態,線程安全)

public class JdbcTemplate {
    // 模板方法:接收回調對象
    public <T> T execute(StatementCallback<T> callback) throws SQLException {
        Connection con = null;
        Statement stmt = null;
        try {
            con = DataSourceUtil.getConnection(); // 1. 獲取連接
            stmt = con.createStatement();         // 2. 創建 Statement
            T result = callback.doInStatement(stmt); // 3. 回調:執行具體 SQL 與結果映射
            return result;
        } catch (SQLException e) {
            throw e;
        } finally {
            // 4. 統一資源關閉 (模板固化)
            closeQuietly(stmt);
            closeQuietly(con);
        }
    }
}

客戶端調用 (Lambda 簡潔表達)

JdbcTemplate template = new JdbcTemplate();
List<User> users = template.execute(stmt -> {
    ResultSet rs = stmt.executeQuery("SELECT * FROM user");
    List<User> list = new ArrayList<>();
    while (rs.next()) {
        User u = new User();
        u.setId(rs.getInt("id"));
        u.setName(rs.getString("name"));
        list.add(u);
    }
    return list;
});

Spring JdbcTemplate 進階封裝:RowMapper & ResultSetExtractor

Spring 進一步將「結果集遍歷」與「單行映射」分離:

  • RowMapper<T>:T mapRow(ResultSet rs, int rowNum) —— 單行映射為對象。
  • ResultSetExtractor<T>:T extractData(ResultSet rs) —— 整個結果集提取為集合/對象。

模板方法內部組合:

public <T> List<T> query(String sql, RowMapper<T> rowMapper) {
    return execute(new ResultSetExtractor<List<T>>() {
        public List<T> extractData(ResultSet rs) {
            List<T> results = new ArrayList<>();
            int rowNum = 0;
            while (rs.next()) {
                results.add(rowMapper.mapRow(rs, rowNum++));
            }
            return results;
        }
    });
}

使用時只需關注單行映射:

List<User> users = jdbcTemplate.query("SELECT * FROM user", (rs, rowNum) -> {
    User u = new User();
    u.setId(rs.getInt("id"));
    u.setName(rs.getString("name"));
    return u;
});

為什麼不直接用繼承式模板方法?

  1. 組合優於繼承:回調接口粒度細,客戶端只實現關心的方法,避免「胖子類」。
  2. 運行時動態性:同一模板實例可配合不同回調完成不同任務,無需創建子類實例。
  3. 函數式編程友好:Java 8+ Lambda 使回調代碼極度簡潔,可讀性大增。
  4. 框架擴展性:Spring 通過 JdbcOperations 接口 + JdbcAccessor 管理 DataSource/異常轉換,構建了完整生態。

總結

「模板方法定義骨架,回調注入變化」是框架設計的經典範式。從 JDBC 模板到事務模板 (TransactionTemplate)、Redis 模板 (RedisTemplate),Spring 全家桶無不體現此思想。掌握此模式,不僅能寫出可復用的工具類,更能深度理解主流框架的擴展機制。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved