模板方法模式一般用于有标准流程性的业务场景,总体流程不变,但流程中的每一个环节可能有所不同,用标准定义来描述就是,模板方法模式定义了一个算法的步骤,并允许子类为一个或多个步骤提供具体的实现,让子类可以在不改变算法架构的情况下,重新定义算法中的某些步骤。

public abstract class AbstractClass {
public final void templateMethod(){
method1();
method2();
method3();
}
protected abstract void method3();
protected final void method2(){
System.out.println("AbstractClass method2");
}
protected abstract void method1();
}
public class ConcreteClassA extends AbstractClass {
@Override
protected void method3() {
System.out.println("ConcreteClassA method3");
}
@Override
protected void method1() {
System.out.println("ConcreteClassA method1");
}
}
public class ConcreteClassB extends AbstractClass {
@Override
protected void method3() {
System.out.println("ConcreteClassB method3");
}
@Override
protected void method1() {
System.out.println("ConcreteClassB method1");
}
}
当业务逻存在一些需要多个步骤来完成,某些步骤是标准统一的,某些步骤根据不同的场景又有不同的处理逻辑,如果全部写在一个类中,可能那些不同场景步骤会出现各种条件判断,无论哪个场景发生修改,都会影响整个流程,这不符合开闭原则,但如果为每个场景都分别实现一套,同样对应相同的步骤就会出现重复的逻辑,又会带来大量的重复代码。
那么,模板方法就是解决上述问题的一种非常好的模式,利用模板方法模式可以实现代码的复用和扩展,这实际上在对代码重构时就非常有帮助。
对于不变的部分,与变的部分做到了隔离,符合开闭原则,使用业务非常容易扩展。
对于公用逻辑易于维护。
由于每一个不同的实现都要一个子类来实现,会导致类的个数变得更多。
AbstractApplicationContext类的refresh方法,这里就用到了模板方法模式,有一些子方法是由AbstractApplicationContext自己实现的,有一些子方法则是让不同的实现类各自实现。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
}
}
}
AbstractController提供了handleRequest方法,负责请求处理并返回ModelAndView,其也是通过模板方法模式来实现,这点作者甚至直接写在了文档说明中。

public abstract class AbstractController extends WebContentGenerator implements Controller {
@Override
@Nullable
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
response.setHeader("Allow", getAllowHeader());
return null;
}
// Delegate to WebContentGenerator for checking and preparing.
checkRequest(request);
prepareResponse(response);
// Execute handleRequestInternal in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return handleRequestInternal(request, response);
}
}
}
return handleRequestInternal(request, response);
}
/**
* Template method. Subclasses must implement this.
* The contract is the same as for {@code handleRequest}.
* @see #handleRequest
*/
@Nullable
protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception;
}
public abstract class InputStream implements Closeable {
// 留给子类实现
public abstract int read() throws IOException;
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
}
其他IO、NIO类库中也有很多类似的设计。
这也是比较经典的应用,其定义的doPost/doGet等方法是可以让子类进行重写的

public abstract class HttpServlet extends GenericServlet
implements java.io.Serializable
{
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
}
BaseExecutor是MyBatis中经典的模板方法模式应用,其主要是用来执行SQL。
query方法可以看作是定义的主流程,doQuery方法是其留给子类实现的。
public abstract class BaseExecutor implements Executor {
// 几个do开头的方法都是留给子类实现的
protected abstract int doUpdate(MappedStatement ms, Object parameter)
throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
throws SQLException;
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
@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 {
// 具体query方式,交由子类实现
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;
}