概述
最近回顧了一下設(shè)計(jì)模式。想到Spring框架中,使用設(shè)計(jì)模式挺多的骇塘。于是搜索了一下Spring中有沒有使用命令模式蘸朋?
參照:命令模式淺析核无,然后對(duì)Spring中的JdbcTemplate類進(jìn)行了源碼閱讀,現(xiàn)在就命令模式藕坯,對(duì)JdbcTemplate中的部分代碼做一下解讀团南。
命令模式簡介
在軟件設(shè)計(jì)中,我們經(jīng)常需要向某些對(duì)象發(fā)送請(qǐng)求炼彪,但是并不知道請(qǐng)求的接收者是誰吐根,也不知道被請(qǐng)求的操作是哪個(gè),
我們只需在程序運(yùn)行時(shí)指定具體的請(qǐng)求接收者即可辐马,此時(shí)拷橘,可以使用命令模式來進(jìn)行設(shè)計(jì),
使得請(qǐng)求發(fā)送者與請(qǐng)求接收者消除彼此之間的耦合喜爷,讓對(duì)象之間的調(diào)用關(guān)系更加靈活冗疮。
舉個(gè)例子吧,將軍發(fā)布命令檩帐,士兵去執(zhí)行术幔。其中有幾個(gè)角色:將軍(命令發(fā)布者)、士兵(命令的具體執(zhí)行者)轿塔、命令(連接將軍和士兵)特愿。
Invoker是調(diào)用者(將軍),Receiver是被調(diào)用者(士兵)勾缭,MyCommand是命令揍障,實(shí)現(xiàn)了Command接口,持有接收對(duì)象
JdbcTemplate部分代碼解析
此類在工作中經(jīng)常被使用俩由。其中的query方法毒嫡,進(jìn)行了大量的重載。
我們拿其中的一個(gè)重載方法來說:
@Override
public <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException {
return query(sql, new RowMapperResultSetExtractor<T>(rowMapper));
}
這個(gè)方法調(diào)用了:
@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());
}
其中,我們發(fā)現(xiàn)有一個(gè)匿名內(nèi)部類:** QueryStatementCallback兜畸,它實(shí)現(xiàn)了 StatementCallback接口努释。
StatementCallback接口中有唯一的doInStatement**方法:
T doInStatement(Statement stmt) throws SQLException, DataAccessException;
我們?cè)俳又驴矗?/p>
public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException
最后,調(diào)用了execute(new QueryStatementCallback())方法咬摇,并且把匿名內(nèi)部類QueryStatementCallback的實(shí)例對(duì)象當(dāng)做參數(shù)傳遞了過去伐蒂。
@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參數(shù)是** StatementCallback**類型。
命令模式角色對(duì)應(yīng)解析
在這個(gè)query查詢中肛鹏,我們可以把 ** StatementCallback接口看做命令接口逸邦。
匿名內(nèi)部類 QueryStatementCallback是該命令接口的一個(gè)具體實(shí)現(xiàn)命令。
在 QueryStatementCallback中在扰,對(duì) doInStatement接口進(jìn)行了重寫缕减,具體實(shí)現(xiàn)了命令的執(zhí)行。(相當(dāng)于執(zhí)行命令的士兵芒珠,這里沒有用具體的類去單獨(dú)寫)
而命令調(diào)用者(將軍)桥狡,是 T execute(StatementCallback<T> action)方法。根據(jù)傳遞的具體命令不同皱卓,最后action.doInStatement(stmtToUse)執(zhí)行的具體命令也就不同裹芝。
其中, QueryStatementCallback的具體實(shí)現(xiàn)類還有以下幾個(gè):
同時(shí)好爬,我們可以看到命令調(diào)用者: T execute(StatementCallback<T> action)**方法調(diào)用時(shí)局雄,也對(duì)應(yīng)傳遞了相應(yīng)的具體命令。
備注
- Spring版本:4.3