模板方法模式
介绍
定义:模板方法模式在一个方法中定义一个算法的骨架,将一些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义该算法的某些步骤。
示例:
/**
* 咖啡因饮料冲泡法
*/
public abstract class CaffeineBeverage {
/**
* 模板方法,咖啡因饮料有相同的制作过程,只是某些步骤不同
*/
final void prepareRecipe() {
// 把水煮沸
boilWater();
// 冲泡
brew();
// 把饮料倒进杯子
pourInCup();
// 加调料
addCondiments();
}
abstract void brew();
abstract void addCondiments();
/**
* 把水煮沸(步骤是通用的,所以放在基类中)
*/
void boilWater() {
System.out.println("Boiling water");
}
/**
* 把饮料倒进杯子
*/
void pourInCup() {
System.out.println("Pouring into cup");
}
}
/**
* 咖啡冲泡法
*/
public class Coffee extends CaffeineBeverage {
/**
* 冲泡咖啡
*/
@Override
void brew() {
System.out.println("Dripping Coffee through filter");
}
/**
* 加入糖和牛奶
*/
@Override
void addCondiments() {
System.out.println("Adding Sugar and Milk");
}
}
/**
* 茶冲泡法
*/
public class Tea extends CaffeineBeverage {
/**
* 冲泡茶叶
*/
@Override
void brew() {
System.out.println("Steeping the tea");
}
/**
* 加入柠檬
*/
@Override
void addCondiments() {
System.out.println("Adding Lemon");
}
}
分析:在上述代码中,CaffeineBeverage
类是一个抽象类,其中prepareRecipe()
方法是一个模板方法,定义了算法的骨架,包含了一些基本的步骤,以及一些需要延迟到子类中实现的抽象方法。在Coffee
和Tea
类中,分别实现了brew()
和addCondiments()
方法,这些方法是具体的步骤,可以根据需要在子类中进行重定义。通过使用模板方法模式,我们可以将算法的结构和具体实现分离开来,从而实现代码的复用和扩展。
示例来自《HeadFirst 设计模式》
在 MyBatis 中的应用
TypeHandler
在 MyBatis 中,TypeHandler 接口用来给 PreparedStatement 设置参数,以及从 ResultSet 获取结果:
public interface TypeHandler<T> {
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
TypeHandlerRegistry 中注册了很多类型处理器,比如 StringTypeHandler、IntegerTypeHandler 等:
public final class TypeHandlerRegistry {
// ...
public TypeHandlerRegistry() {
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
// ...
}
// ...
}
这些类型处理器都继承自 BaseTypeHandler,BaseTypeHandler 实现了 TypeHandler 接口中的方法,但是仅定义了通用的步骤,具体的实现由子类完成:
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
protected Configuration configuration;
public void setConfiguration(Configuration c) {
this.configuration = c;
}
@Override
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
if (jdbcType == null) {
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException e) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
"Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
"Cause: " + e, e);
}
} else {
try {
// ⭐ 设置非 null 参数(子类实现)
setNonNullParameter(ps, i, parameter, jdbcType);
} catch (Exception e) {
throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
"Try setting a different JdbcType for this parameter or a different configuration property. " +
"Cause: " + e, e);
}
}
}
@Override
public T getResult(ResultSet rs, String columnName) throws SQLException {
T result;
try {
// ⭐ 获取非 null 结果(子类实现)
result = getNullableResult(rs, columnName);
} catch (Exception e) {
throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e);
}
if (rs.wasNull()) {
return null;
} else {
return result;
}
}
// ...
/**
* ⭐ 设置非 null 参数(子类实现)
*/
public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
/**
* ⭐ 获取非 null 结果(子类实现)
*/
public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
// ...
}
比如 StringTypeHandler 类继承了 BaseTypeHandler 类,并实现了 getNullableResult() 等方法:
public class StringTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
throws SQLException {
ps.setString(i, parameter);
}
@Override
public String getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return rs.getString(columnName);
}
// ...
}
这样做的好处是各种类型的 TypeHandler 都可以通过继承 BaseTypeHandler 类来实现自己的处理逻辑,同时又复用 BaseTypeHandler 类中通用的步骤,既易于扩展,又易于维护。
StatementHandler
MyBatis StatementHandler 接口主要负责 SQL 语句的执行和参数的设置:
public interface StatementHandler {
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
void parameterize(Statement statement)
throws SQLException;
void batch(Statement statement)
throws SQLException;
int update(Statement statement)
throws SQLException;
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
// ...
}
BaseStatementHandler 类是 StatementHandler 接口的抽象实现类,它定义了 StatementHandler 接口需要实现的大部分方法,包括对参数的设置、SQL 语句的执行等,并提供了一个抽象方法 instantiateStatement(Connection connection),供子类实例化 Statement 对象:
public abstract class BaseStatementHandler implements StatementHandler {
// ...
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
// ⭐ 实例化 Statement 对象(子类实现)
statement = instantiateStatement(connection);
setStatementTimeout(statement, transactionTimeout);
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
/**
* ⭐ 实例化 Statement 对象(子类实现)
*/
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
// ...
}
StatementHandler 的实现类 SimpleStatementHandler、PreparedStatementHandler 和 CallableStatementHandler 分别对应不同的 Statement 对象,比如 SimpleStatementHandler 使用 Statement 对象执行 SQL 语句,PreparedStatementHandler 使用 PreparedStatement 对象执行 SQL 语句,CallableStatementHandler 使用 CallableStatement 对象执行 SQL 语句,它们都继承自 BaseStatementHandler 类,并实现了不同的 instantiateStatement() 方法,以 PreparedStatement 为例:
public class PreparedStatementHandler extends BaseStatementHandler {
// ...
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() != null) {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.prepareStatement(sql);
}
}
// ...
}
可以看到,PreparedStatementHandler 通过调用 connection.prepareStatement() 方法实例化了一个 PreparedStatement 对象,并返回了该对象。
在这里,BaseStatementHandler 中的 prepare() 方法就是模板方法,其中定义了 prepare(准备)的通用步骤,instantiateStatement(实例化 Statement 对象)的细节交给子类实现。
Executor
Executor 接口是 MyBatis 最核心的接口之一,它定义了执行 SQL 语句的方法,包括查询、更新等操作:
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement ms, Object parameter) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
// ...
}
BaseExecutor 类是 Executor 接口的抽象实现类,它定义了 Executor 接口需要实现的大部分方法,包括对 SQL 语句的更新和查询等操作,并提供了抽象方法 doUpdate() 和 doQuery(),供子类实现:
public abstract class BaseExecutor implements Executor {
@Override
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
clearLocalCache();
// ⭐ 执行 update 语句(子类实现)
return doUpdate(ms, parameter);
}
/**
* ⭐ 执行 update 语句(子类实现)
*/
protected abstract int doUpdate(MappedStatement ms, Object parameter)
throws SQLException;
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// ⭐ 缓存未命中,从数据库中查询数据
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
// ⭐ 从数据库中查询数据(子类实现)
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
/**
* ⭐ 从数据库中查询数据(子类实现)
*/
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
}
Executor 的实现类 SimpleExecutor、ClosedExecutor、ReuseExecutor、BatchExecutor 都继承自 BaseExecutor,并提供了具体的实现方法 doUpdate() 和 doQuery()。比如 SimpleExecutor 的 doUpdate 方法如下:
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
可以看到,最终调用的是 StatementHandler 的 update() 方法。