从豆浆机到MyBatis模板方法模式在主流Java框架里的“隐形”应用当你清晨用豆浆机制作不同口味的饮品时是否想过这和Java框架的设计思想竟有异曲同工之妙选豆、浸泡、研磨这些固定流程就像代码中的算法骨架而添加红豆或花生则如同子类对特定步骤的个性化实现。这种固定流程可变细节的智慧正是模板方法模式Template Method Pattern的精髓所在。在Java生态中许多看似复杂的框架实现其实都藏着这种设计模式的巧妙应用。本文将带你在MyBatis、Spring等主流框架的源码中进行一次寻宝之旅揭示大师们如何用模板方法模式构建既稳定又灵活的架构。无论你是想深入理解框架原理还是希望提升自己的代码设计能力这些隐藏在常见API背后的模式应用都值得细细品味。1. 模板方法模式的核心哲学模板方法模式本质上是一种好莱坞原则Dont call us, well call you的体现——父类控制整体流程子类只需关注自己需要改变的部分。这种反向控制的结构让代码既保持了统一性又具备了扩展性。1.1 模式结构解析典型的模板方法模式包含以下关键组件public abstract class AbstractTemplate { // 模板方法定义算法骨架 public final void templateMethod() { step1(); step2(); step3(); } // 固定实现步骤 private void step1() { System.out.println(执行固定步骤1); } // 抽象方法由子类实现 protected abstract void step2(); // 钩子方法可选覆盖 protected void step3() { System.out.println(默认步骤3实现); } }这种结构带来了三个显著优势代码复用将不变部分集中到父类避免重复代码扩展可控子类只能修改允许变化的部分不会破坏整体结构逻辑清晰算法流程一目了然维护者能快速理解整体逻辑1.2 现实世界的类比理解设计模式最好的方式就是从生活场景寻找对应关系生活场景对应代码概念咖啡制作流程算法骨架选择咖啡豆品种子类具体实现自定义糖量钩子方法咖啡机操作手册模板方法中的固定步骤这种将现实世界抽象为代码模式的能力正是区分普通开发者和架构师的重要标志。2. MyBatis中的执行器体系MyBatis作为ORM框架的核心价值之一就是将对数据库的操作抽象为统一的执行流程。深入其Executor接口的实现你会发现这正是模板方法模式的经典应用场景。2.1 BaseExecutor的模板设计BaseExecutor作为所有执行器的基类定义了数据库操作的标准流程public abstract class BaseExecutor implements Executor { // 查询模板方法 public E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) { // 1. 获取绑定SQL BoundSql boundSql ms.getBoundSql(parameter); // 2. 创建缓存Key CacheKey key createCacheKey(ms, parameter, rowBounds, boundSql); // 3. 执行查询抽象方法 return queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } // 由子类实现的抽象方法 protected abstract E ListE queryFromDatabase(...); }这种设计确保了所有执行器都遵循相同的缓存处理逻辑子类只需关注具体的数据库交互实现新增执行器类型时不会破坏现有流程2.2 不同执行器的实现对比MyBatis提供了多种执行器实现它们在模板方法的基础上各自扩展执行器类型扩展点实现应用场景SimpleExecutor每次执行都创建新Statement默认配置ReuseExecutor复用预处理Statement高频相同SQL场景BatchExecutor批量操作优化大批量数据写入源码阅读技巧在IDE中查看BaseExecutor类注意哪些方法是final的不可修改流程哪些是abstract的必须实现哪些是普通方法可选覆盖。3. Spring框架中的模板应用Spring框架堪称设计模式的百科全书其中模板方法模式的应用更是随处可见。让我们聚焦两个典型案例JdbcTemplate和事务管理。3.1 JdbcTemplate的callback机制JdbcTemplate通过回调接口将可变部分抽象出来保持核心流程不变public class JdbcTemplate { public T T query(String sql, ResultSetExtractorT rse) { // 1. 获取连接 Connection con DataSourceUtils.getConnection(getDataSource()); try { // 2. 准备语句 Statement stmt con.createStatement(); try { // 3. 执行查询 ResultSet rs stmt.executeQuery(sql); try { // 4. 处理结果回调扩展点 return rse.extractData(rs); } finally { rs.close(); } } finally { stmt.close(); } } finally { DataSourceUtils.releaseConnection(con, getDataSource()); } } }这种设计带来了极佳的灵活性开发者可以自定义ResultSetExtractor实现特殊结果处理Spring内置了RowMapper等常用回调接口资源管理连接、语句、结果集由模板统一处理3.2 事务管理的模板流程Spring事务抽象的核心类PlatformTransactionManager也采用了模板方法public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager { public final TransactionStatus getTransaction(...) { // 1. 获取事务 Object transaction doGetTransaction(); // 2. 检查是否存在事务 if (isExistingTransaction(transaction)) { // 处理已存在事务 return handleExistingTransaction(...); } // 3. 检查超时设置 if (definition.getTimeout() TransactionDefinition.TIMEOUT_DEFAULT) { throw new InvalidTimeoutException(...); } // 4. 处理新事务抽象方法 return startTransaction(definition, transaction, debugEnabled, suspendedResources); } protected abstract Object doGetTransaction(); protected abstract void doBegin(...); protected abstract void doCommit(...); }这种架构使得事务获取和提交的标准流程得以统一不同数据源只需实现特定的几个方法新增事务管理器时不会引入流程不一致的风险4. Servlet API的设计智慧即使是最基础的Java Web开发也离不开模板方法模式的应用。HttpServlet类就是最直观的例子。4.1 HttpServlet的服务流程HttpServlet通过模板方法将HTTP请求分发给对应的处理方法public abstract class HttpServlet extends GenericServlet { protected void service(HttpServletRequest req, HttpServletResponse resp) { String method req.getMethod(); if (method.equals(GET)) { doGet(req, resp); } else if (method.equals(POST)) { doPost(req, resp); } // ...其他方法处理 } protected void doGet(HttpServletRequest req, HttpServletResponse resp) { // 默认实现返回405错误 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); } protected void doPost(HttpServletRequest req, HttpServletResponse resp) { // 同上 } }这种设计实现了统一的HTTP方法分发逻辑子类只需覆盖关心的请求方法默认提供合理的错误处理4.2 框架中的过滤器链Servlet规范中的FilterChain同样体现了模板方法思想public interface FilterChain { void doFilter(ServletRequest request, ServletResponse response); } public class ApplicationFilterChain implements FilterChain { private int pos 0; private int n 0; private Filter[] filters; public void doFilter(ServletRequest request, ServletResponse response) { if (pos n) { Filter filter filters[pos]; filter.doFilter(request, response, this); } else { // 执行实际servlet servlet.service(request, response); } } }每个过滤器只需关注自己的处理逻辑而过滤器之间的调用顺序由FilterChain模板控制这种设计让过滤器组合变得非常灵活。5. 日常开发中的模式实践理解了框架中的设计模式后我们应该思考如何将这种思维应用到日常开发中。以下是三个典型的应用场景。5.1 报表生成流程抽象假设我们需要实现多种格式的报表导出功能public abstract class ReportGenerator { public final void generateReport(ReportData data, OutputStream out) { validateData(data); ReportTemplate template prepareTemplate(data); renderReport(template, out); afterGenerate(data); } protected void validateData(ReportData data) { // 基础校验逻辑 } protected abstract ReportTemplate prepareTemplate(ReportData data); protected abstract void renderReport(ReportTemplate template, OutputStream out); protected void afterGenerate(ReportData data) { // 默认空实现 } }这样新增报表类型时只需继承并实现关键步骤public class PdfReportGenerator extends ReportGenerator { protected ReportTemplate prepareTemplate(ReportData data) { // PDF模板准备逻辑 } protected void renderReport(ReportTemplate template, OutputStream out) { // 使用iText等库渲染PDF } }5.2 支付网关集成支付接口通常有相似的流程参数校验→加密→请求→结果处理public abstract class PaymentGateway { public final PaymentResult pay(PaymentRequest request) { validate(request); String encrypted encrypt(request); String response sendRequest(encrypted); return processResponse(response); } protected abstract String encrypt(PaymentRequest request); protected abstract String sendRequest(String encryptedData); protected abstract PaymentResult processResponse(String response); private void validate(PaymentRequest request) { // 通用校验逻辑 } }5.3 业务规则引擎对于需要灵活扩展的业务规则处理public abstract class BusinessRuleEngine { public final RuleExecutionResult execute(RuleContext context) { preProcess(context); RuleExecutionResult result applyRules(context); postProcess(result); return result; } protected void preProcess(RuleContext context) { // 上下文预处理 } protected abstract RuleExecutionResult applyRules(RuleContext context); protected void postProcess(RuleExecutionResult result) { // 默认空实现 } }6. 模式应用的注意事项虽然模板方法模式非常实用但在实际应用中也需要考虑以下几点6.1 何时使用模板方法适合采用模板方法模式的场景通常具有以下特征存在多个相似流程主要步骤相同但某些细节不同需要严格控制流程执行顺序希望避免重复代码同时保持扩展性子类只需要改变算法的部分内容而非全部6.2 潜在的设计陷阱过度使用模板方法可能导致类层次过深增加系统复杂度父类变得过于庞大承担过多责任子类被迫实现不需要的方法可用钩子方法缓解6.3 与其他模式的关系模板方法模式常与其他模式配合使用工厂方法模式模板方法中的某些步骤可能使用工厂方法创建对象策略模式可以用策略模式替换模板方法中的某些步骤观察者模式模板方法中的某些步骤可能触发观察者通知在MyBatis的Executor实现中实际上就结合了模板方法和装饰器模式通过CachingExecutor装饰基础执行器。