定义:定义一个算法的框架,并允许子类提供框架中一个或多个步骤的具体实现。模版方法将算法的步骤实现交由子类决定,并且不会影响算法结构。
代码示例
/**
* 模版抽象类,定义算法结构
*/
public abstract class AbstractClass{
// 也可定义为抽象方法,每个抽象方法都需要实现
protected void method1(){};
protected void method2(){};
public final void templateMethod(){
this.method1();
this.method2();
}
}
/**
* 子类实现某个步骤
*/
public class ConcreteClass extends AbstractClass{
@Override
protected void method1(){};
}
public static void main(String[] args) {
AbstractClass abstractClass=new ConcreteClass();
abstractClass.templateMethod();
}
来看下spring中模版方法的使用
JdbcTemplate
:
//留意到JdbcTemplate的继承关系
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
// 主要看这个方法讲解,实现JdbcOperations的接口
@Override
@Nullable
public <T> T execute(StatementCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Connection con = DataSourceUtils.getConnection(obtainDataSource());
Statement stmt = null;
try {
stmt = con.createStatement();
applyStatementSettings(stmt);
// 模版的调用在这句
T result = action.doInStatement(stmt);
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.
String sql = getSql(action);
JdbcUtils.closeStatement(stmt);
stmt = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw translateException("StatementCallback", sql, ex);
}
finally {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
}
在来看下StatementCallback
接口,从注释就能明白这个接口的作用,以及JdbcTemplate所负责的功能。不得不强调良好的注释,能增加源码的可读性。
@FunctionalInterface
public interface StatementCallback<T> {
/**
* Gets called by {@code JdbcTemplate.execute} with an active JDBC
* Statement. Does not need to care about closing the Statement or the
* Connection, or about handling transactions: this will all be handled
* by Spring's JdbcTemplate.
* <p><b>NOTE:</b> Any ResultSets opened should be closed in finally blocks
* within the callback implementation. Spring will close the Statement
* object after the callback returned, but this does not necessarily imply
* that the ResultSet resources will be closed: the Statement objects might
* get pooled by the connection pool, with {@code close} calls only
* returning the object to the pool but not physically closing the resources.
* <p>If called without a thread-bound JDBC transaction (initiated by
* DataSourceTransactionManager), the code will simply get executed on the
* JDBC connection with its transactional semantics. If JdbcTemplate is
* configured to use a JTA-aware DataSource, the JDBC connection and thus
* the callback code will be transactional if a JTA transaction is active.
* <p>Allows for returning a result object created within the callback, i.e.
* a domain object or a collection of domain objects. Note that there's
* special support for single step actions: see JdbcTemplate.queryForObject etc.
* A thrown RuntimeException is treated as application exception, it gets
* propagated to the caller of the template.
* @param stmt active JDBC Statement
* @return a result object, or {@code null} if none
* @throws SQLException if thrown by a JDBC method, to be auto-converted
* to a DataAccessException by an SQLExceptionTranslator
* @throws DataAccessException in case of custom exceptions
* @see JdbcTemplate#queryForObject(String, Class)
* @see JdbcTemplate#queryForRowSet(String)
*/
@Nullable
T doInStatement(Statement stmt) throws SQLException, DataAccessException;
}
那么JdbcTemplate
如何调用execute方法的呢,且看query方法
@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;
}
}
// 提供具体实现类,实现execute某一步骤的自定义
return execute(new QueryStatementCallback());
}
可以看到StatementCallback的实现类:
image.png
Spring中1以Template结尾命名的类都是用的模板方法模式,同样的影子在RestTemplate、RedisTemplate等类中也能发现。
网友评论