← 返回题目列表

模板方法模式在 JDK 和 Spring 中有哪些典型应用?

高频 中等 第 10 / 26 题 更新于 2026/07/28
模板方法模式JDKSpring框架源码

简化版

模板方法模式在框架中很常见,因为框架通常负责控制主流程,业务代码只实现扩展点。典型例子包括 Spring 的 JdbcTemplateRestTemplate 部分调用流程、Servlet 的 service/doGet/doPost 思想,以及测试框架中的生命周期方法。

详细版

模板方法模式非常适合框架设计。框架先定义稳定流程,再把变化点留给用户代码。

常见例子:

  1. JdbcTemplate:封装获取连接、创建语句、执行 SQL、处理异常、释放资源等流程,调用方只关注 SQL 和结果映射。
  2. Servlet:容器调用 service(),再根据 HTTP 方法分发到 doGet()doPost() 等方法。
  3. 测试框架:统一执行测试生命周期,用户通过 setup、test、teardown 等扩展点补充逻辑。
  4. Spring Bean 生命周期:容器控制创建、依赖注入、初始化、销毁流程,业务对象通过回调参与部分阶段。

这些例子的共同点是:框架控制流程,业务代码填补变化点。

完整版教学

一、为什么框架特别喜欢模板方法

框架的职责通常是控制复杂流程,屏蔽通用细节。

比如数据库访问有很多固定工作:

  • 获取连接;
  • 创建语句;
  • 执行 SQL;
  • 处理结果;
  • 处理异常;
  • 释放资源。

如果每个业务方法都手写这些逻辑,重复又容易出错。框架用模板方法把公共流程固定下来,让业务代码只关注真正变化的部分。

二、JdbcTemplate 是经典例子

JdbcTemplate 的思想就是模板方法风格:它封装 JDBC 样板代码,让调用方只提供 SQL、参数和结果映射。

调用方常写:

jdbcTemplate.query(sql, rowMapper);

背后流程大致是:

获取连接 -> 创建 PreparedStatement -> 设置参数 -> 执行查询 -> 遍历 ResultSet -> 调用 RowMapper -> 释放资源

其中资源管理、异常转换、连接处理由模板负责;每一行怎么映射成对象,由调用方提供。

严格说,JdbcTemplate 也结合了回调思想,但整体上体现了“模板控制流程,用户提供变化点”的模板方法思想。

三、Servlet 的 service/doGet/doPost 也很好理解

Servlet 容器接收请求后,会调用 Servlet 的 service() 方法。

service() 根据 HTTP 方法分发到:

  • doGet()
  • doPost()
  • doPut()
  • doDelete()

开发者通常重写 doGet()doPost() 来实现业务逻辑。

这体现了模板方法思想:容器和父类控制请求处理主流程,子类实现具体请求方法处理。

四、Spring 生命周期也有模板思想

Spring 容器创建 Bean 的流程是稳定的:

实例化 -> 属性填充 -> 初始化前处理 -> 初始化 -> 初始化后处理 -> 使用 -> 销毁

业务代码可以通过接口、注解或后置处理器参与某些阶段。例如初始化回调、销毁回调、BeanPostProcessor。

虽然这不是最传统的继承版模板方法,但体现了框架控制流程、业务扩展步骤的同类思想。

五、面试回答要避免说得太绝对

有些框架源码不是纯粹的 GoF 模板方法模式,而是模板方法、回调、策略、工厂等模式组合使用。

例如 JdbcTemplate 里既有模板流程,也大量使用回调接口。面试中更稳妥的表述是:它体现了模板方法的思想,而不是强行说它只用了模板方法模式。

这会显得更准确。

六、框架中的模板与回调变体

JdbcTemplate 固定资源获取、执行、异常转换、释放 4 类步骤,调用者通常只提供 SQL 和回调;一次异常仍由模板保证连接释放。

JdbcTemplate.execute -> acquire -> callback.doInConnection -> translate -> release

这里真正需要保护的是父类掌握的流程不变量:客户端只能进入模板入口,子类只能改写约定好的步骤。评审时应逐步标出哪些行为固定、哪些行为必须实现、哪些行为只是可选钩子,避免父类和子类互相猜测。

七、继承边界与流程契约验证

检查维度应确认的内容
机制正确性Servlet 的 service 分派和 Spring Template 类体现不同程度的骨架控制,要说清实际扩展点。
适用边界框架可能用回调对象而非子类覆写实现变化点,这属于模板思想与回调组合,回答时不要硬说所有例子都严格对应 GoF 类图。
测试证据用框架真实入口触发模板,验证回调时机、异常转换和资源清理,不能只直接调用扩展方法
工程代价重点评估父类改动会影响多少子类、protected 扩展面有多大,以及新增变化是否迫使无关子类一起修改

易错点:Servlet 的 service 分派和 Spring Template 类体现不同程度的骨架控制,要说清实际扩展点。

八、常见误区与追问

  • 误区:JdbcTemplate 要求业务类继承它。 常见用法是组合 JdbcTemplate 并传入回调,模板思想存在但扩展机制不必是业务子类继承。
  • 误区:模板方法要求所有步骤都由子类覆写。 稳定步骤应在父类直接实现;只有必要变化点才做抽象步骤,可选变化点才做有默认实现的钩子。
  • 追问:HttpServlet 的模板入口是什么? 容器调用 service,HttpServlet 再根据 HTTP 方法分派到 doGet、doPost 等受保护扩展点。
  • 追问:模板方法为什么常声明为 final? 为了防止子类改写算法骨架和步骤顺序;若框架有意允许重写,则必须明确不变量和扩展契约。
  • 追问:模板方法最关键的回归测试是什么? 是父类契约测试:所有子类都必须满足固定步骤顺序、公共前后置行为和异常清理规则。

九、加强记忆

框架中的模板方法思想可以记成“框架定流程,业务填空”。JDK、Servlet、Spring、JdbcTemplate 都能看到类似影子:通用流程由框架掌控,变化点通过继承、回调或接口留给业务代码。