1. 模板方法模式的核心思想
第一次接触模板方法模式时,我正被一个重复代码问题困扰着。项目中需要处理多种文件导入流程,每种文件的校验规则不同,但解析、日志记录、异常处理的步骤却完全一致。当时写了大量重复代码,直到发现Spring框架中JdbcTemplate类的实现方式,才恍然大悟——这不就是模板方法模式的经典应用吗?
模板方法模式的核心在于定义算法骨架。举个生活中的例子,就像泡茶的固定流程:烧水→放茶叶→冲泡→倒出,其中只有"放茶叶"这个步骤会根据茶叶种类变化。在代码中体现为抽象类定义模板方法(泡茶流程),具体步骤(放什么茶叶)交给子类实现。
这种模式在Java生态中随处可见:
- Spring的JdbcTemplate封装了获取连接、执行SQL、释放连接等固定流程
- MyBatis的BaseExecutor处理了缓存查询、生成语句等通用逻辑
- Servlet规范中的HttpServlet定义了service()方法骨架
// 典型模板方法结构示例 public abstract class FileImporter { // 模板方法(final防止子类修改流程) public final void importProcess() { validateHeader(); parseData(); // 抽象方法 saveLog(); handleException(); } private void validateHeader() { System.out.println("校验文件头信息"); } protected abstract void parseData(); private void saveLog() { System.out.println("记录导入日志"); } }2. Spring框架中的实战案例
在Spring 5.3.18源码中,JdbcTemplate的query()方法完美展示了模板方法模式。我调试过整个执行流程:获取连接→创建语句→执行查询→处理结果集→释放资源,这些固定操作被封装在模板方法中,而RowMapper回调接口允许我们自定义结果集处理。
更精妙的是Spring事务管理的AbstractPlatformTransactionManager:
- 定义了标准事务流程:开启→执行业务→提交/回滚
- 将获取事务、提交等具体操作抽象为doBegin()、doCommit()等方法
- 不同数据源只需继承并实现这些抽象方法
// 模拟Spring事务模板 public abstract class AbstractTransaction { public final void execute() { try { doBegin(); // 抽象方法 businessLogic(); doCommit(); // 抽象方法 } catch (Exception e) { doRollback(); // 抽象方法 } } protected abstract void doBegin(); protected abstract void doCommit(); protected abstract void doRollback(); private void businessLogic() { System.out.println("执行业务代码"); } }实际项目中,我曾用这种模式统一处理Redis缓存操作。模板方法封装了缓存查询→命中判断→反序列化流程,子类只需实现具体的反序列化逻辑,代码复用率提升了70%。
3. 微服务架构中的典型应用
在电商微服务架构中,订单处理流程是个经典场景。不同业务线(普通订单、预售订单、拼团订单)的核心处理逻辑不同,但都需要经历:风控检查→库存锁定→支付触发→状态更新这些步骤。我们团队通过模板方法模式将公共流程收拢到OrderProcessTemplate基类,各业务线只需关注自己的特殊逻辑。
分布式任务调度系统也适用这个模式。比如定时任务的标准流程:
- 从数据库获取任务列表
- 获取分布式锁
- 执行具体任务(抽象方法)
- 释放锁并记录日志
public abstract class DistributedJob { public final void executeJob() { List<Job> jobs = fetchJobs(); for (Job job : jobs) { if (acquireLock(job.getId())) { try { processJob(job); // 抽象方法 } finally { releaseLock(job.getId()); } } } } protected abstract void processJob(Job job); private List<Job> fetchJobs() { // 查询待处理任务 } private boolean acquireLock(String jobId) { // 获取Redis分布式锁 } }实际开发中遇到过坑点:如果子类在实现抽象方法时抛出异常,可能导致锁未释放。后来我们在模板方法中添加了try-finally块,这就是模板方法的优势——集中维护公共逻辑。
4. 高级技巧与最佳实践
钩子方法(Hook Method)是模板方法的进阶玩法。在电商优惠券系统中,我们设计了这样的结构:
public abstract class CouponTemplate { public final void process() { validate(); calculateDiscount(); // 抽象方法 if (needRecordLog()) { // 钩子方法 saveLog(); } } protected boolean needRecordLog() { return true; // 默认记录日志 } protected abstract void calculateDiscount(); }这样设计带来两个好处:
- 普通优惠券自动记录日志
- 特殊优惠券通过重写needRecordLog()关闭日志
性能优化方面要注意:
- 避免在模板方法中做耗时操作
- 抽象方法尽量设计为无状态
- 考虑使用ThreadLocal传递上下文
在DDD架构中,可以将模板方法与策略模式结合。比如支付处理:
- 模板方法定义支付流程:验证→执行→回调
- 策略模式实现不同支付方式(支付宝、微信)
- 子类通过组合支付策略实现具体逻辑
5. 源码级调试技巧
阅读框架源码时,我习惯用IDEA的调试功能追踪模板方法执行流程。以MyBatis的BaseExecutor为例:
- 在query()方法打条件断点
- 观察SqlSession如何调用模板方法
- 查看SimpleExecutor/ReuseExecutor等子类实现
调试Spring的JdbcTemplate时,重点关注:
- 如何通过RowMapper回调实现结果处理
- 资源清理如何在finally块中保证执行
- 异常如何被统一转换为DataAccessException
通过源码学习,我总结出模板方法的三个黄金规则:
- 模板方法应该是final的
- 抽象方法数量要严格控制
- 子类不应该知道其他子类的存在
6. 复杂业务场景实战
在金融风控系统中,我们设计了多级审批模板:
public abstract class RiskControlTemplate { public final void process() { initContext(); checkRuleA(); checkRuleB(); // 抽象方法 if (needExtraCheck()) { checkRuleC(); } persistResult(); } protected boolean needExtraCheck() { return false; } protected abstract void checkRuleB(); }这种结构带来三个优势:
- 新人快速上手,只需关注具体规则实现
- 审批流程变更只需修改基类
- 通过钩子方法支持灵活扩展
在物联网设备管理中,我们将设备上报数据处理抽象为:
- 数据解码(抽象方法)
- 数据校验(模板方法)
- 持久化存储(模板方法)
- 事件触发(钩子方法控制)
7. 设计模式组合应用
模板方法常与其他模式联用:
- 配合工厂方法模式:在Spring的Bean初始化中,AbstractAutowireCapableBeanFactory定义了初始化流程,具体实例化通过createBeanInstance()工厂方法实现
- 配合策略模式:JVM的类加载机制中,ClassLoader定义了双亲委派模板,具体查找逻辑由findClass()策略实现
- 配合观察者模式:JavaFX的Application类定义了应用生命周期模板,通过init()、start()等抽象方法支持扩展
在微服务网关开发中,我们这样组合模式:
- 模板方法定义过滤器链流程
- 策略模式实现不同路由策略
- 装饰器模式增强请求处理
public abstract class ApiGatewayFilter { public final void handle(Request request) { auth(request); transform(request); // 抽象方法 if (shouldCache(request)) { cache(request); } dispatch(request); } protected abstract void transform(Request request); protected boolean shouldCache(Request request) { return false; } }这些实战经验让我深刻体会到:模板方法模式不是简单的代码复用工具,而是架构设计中的重要思想。它帮助我们在保持架构统一性的同时,又不失灵活性。