天天看點

指令模式-Spring架構JdbcTemplate

概述

最近回顧了一下設計模式。想到Spring架構中,使用設計模式挺多的。于是搜尋了一下Spring中有沒有使用指令模式?

參照:指令模式淺析,然後對Spring中的JdbcTemplate類進行了源碼閱讀,現在就指令模式,對JdbcTemplate中的部分代碼做一下解讀。

指令模式簡介

在軟體設計中,我們經常需要向某些對象發送請求,但是并不知道請求的接收者是誰,也不知道被請求的操作是哪個,

我們隻需在程式運作時指定具體的請求接收者即可,此時,可以使用指令模式來進行設計,

使得請求發送者與請求接收者消除彼此之間的耦合,讓對象之間的調用關系更加靈活。

舉個例子吧,将軍釋出指令,士兵去執行。其中有幾個角色:将軍(指令釋出者)、士兵(指令的具體執行者)、指令(連接配接将軍和士兵)。

Invoker是調用者(将軍),Receiver是被調用者(士兵),MyCommand是指令,實作了Command接口,持有接收對象

JdbcTemplate部分代碼解析

此類在工作中經常被使用。其中的query方法,進行了大量的重載。

我們拿其中的一個重載方法來說:

@Override
public <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException {
    return query(sql, new RowMapperResultSetExtractor<T>(rowMapper));
}
           

這個方法調用了:

@Override
    public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
        Assert.notNull(sql, "SQL must not be null");
        Assert.notNull(rse, "ResultSetExtractor must not be null");
        if (logger.isDebugEnabled()) {
            logger.debug("Executing SQL query [" + sql + "]");
        }
        class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
            @Override
            public T doInStatement(Statement stmt) throws SQLException {
                ResultSet rs = null;
                try {
                    rs = stmt.executeQuery(sql);
                    ResultSet rsToUse = rs;
                    if (nativeJdbcExtractor != null) {
                        rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
                    }
                    return rse.extractData(rsToUse);
                }
                finally {
                    JdbcUtils.closeResultSet(rs);
                }
            }
            @Override
            public String getSql() {
                return sql;
            }
        }
        return execute(new QueryStatementCallback());
    }
           

其中,我們發現有一個匿名内部類:** QueryStatementCallback,它實作了 StatementCallback接口。

StatementCallback接口中有唯一的doInStatement**方法:

T doInStatement(Statement stmt) throws SQLException, DataAccessException;
           

我們再接着往下看,

public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException
           

最後,調用了execute(new QueryStatementCallback())方法,并且把匿名内部類QueryStatementCallback的執行個體對象當做參數傳遞了過去。

@Override
    public <T> T execute(StatementCallback<T> action) throws DataAccessException {
        Assert.notNull(action, "Callback object must not be null");
    Connection con = DataSourceUtils.getConnection(getDataSource());
    Statement stmt = null;
    try {
        Connection conToUse = con;
        if (this.nativeJdbcExtractor != null &&
                this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {
            conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
        }
        stmt = conToUse.createStatement();
        applyStatementSettings(stmt);
        Statement stmtToUse = stmt;
        if (this.nativeJdbcExtractor != null) {
            stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);
        }
        T result = action.doInStatement(stmtToUse);
        handleWarnings(stmt);
        return result;
    }
    catch (SQLException ex) {
        // Release Connection early, to avoid potential connection pool deadlock
        // in the case when the exception translator hasn't been initialized yet.
        JdbcUtils.closeStatement(stmt);
        stmt = null;
        DataSourceUtils.releaseConnection(con, getDataSource());
        con = null;
        throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);
    }
    finally {
        JdbcUtils.closeStatement(stmt);
        DataSourceUtils.releaseConnection(con, getDataSource());
    }
}
           

我們着重看一下這一行代碼:

T result = action.doInStatement(stmtToUse);
           

其中action參數是** StatementCallback**類型。

指令模式角色對應解析

在這個query查詢中,我們可以把 ** StatementCallback接口看做指令接口。

匿名内部類 QueryStatementCallback是該指令接口的一個具體實作指令。

在 QueryStatementCallback中,對 doInStatement接口進行了重寫,具體實作了指令的執行。(相當于執行指令的士兵,這裡沒有用具體的類去單獨寫)

而指令調用者(将軍),是 T execute(StatementCallback action)方法。根據傳遞的具體指令不同,最後action.doInStatement(stmtToUse)執行的具體指令也就不同。

其中, QueryStatementCallback的具體實作類還有以下幾個:

指令模式-Spring架構JdbcTemplate

同時,我們可以看到指令調用者: T execute(StatementCallback action)**方法調用時,也對應傳遞了相應的具體指令。

指令模式-Spring架構JdbcTemplate

備注

Spring版本:4.3