Spring源码中的模板方法模式
1. 什么是模板方法模式
模板方法模式(Template Method Pattern)是一种行为设计模式,它在操作中定义算法的框架,将一些步骤推迟到子类中。模板方法让子类在不改变算法结构的情况下重新定义算法的某些步骤。

模板方法模式的定义:
在操作中定义算法的框架,并将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下重新定义算法的某些步骤。
模板方法中的算法可以理解为广义上的业务逻辑,并不是特指某一个实际的算法。定义中所说的算法的框架就是模板,包含算法框架的方法就是模板方法。

模板方法模式包含以下主要角色:
- 抽象父类:定义一个算法所包含的所有步骤,并提供一些通用的方法逻辑。
- 具体子类:继承自抽象父类,根据需要重写父类提供的算法步骤中的某些步骤。
抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。
-
模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
-
基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:
- 抽象方法(Abstract Method):一个抽象方法由抽象类声明、由其具体子类实现。
- 具体方法(Concrete Method):一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。
- 钩子方法(Hook Method):在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。
钩子:在模板方法的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”。钩子方法一般是空的或者有默认实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。而要不要挂钩,又由子类去决定。
2. 代码示例
/*** 抽象父类*/
public abstract class AbstractClassTemplate {void step1(String key) {System.out.println("在模板类中 -> 执行步骤1");if (step2(key)) {step3();} else {step4();}step5();}boolean step2(String key) {System.out.println("在模板类中 -> 执行步骤2");return "x".equals(key);}abstract void step3();abstract void step4();void step5() {System.out.println("在模板类中 -> 执行步骤5");}void run(String key) {step1(key);}
}public class ConcreteClassA extends AbstractClassTemplate {@Overridevoid step3() {System.out.println("在子类A中 -> 执行步骤 3");}@Overridevoid step4() {System.out.println("在子类A中 -> 执行步骤 4");}
}public class ConcreteClassB extends AbstractClassTemplate {@Overridevoid step3() {System.out.println("在子类B中 -> 执行步骤 3");}@Overridevoid step4() {System.out.println("在子类B中 -> 执行步骤 4");}
}public class Test01 {public static void main(String[] args) {AbstractClassTemplate concreteClassA = new ConcreteClassA();concreteClassA.run("");System.out.println("===========");AbstractClassTemplate concreteClassB = new ConcreteClassB();concreteClassB.run("x");}
}// 输出结果
在模板类中 -> 执行步骤1
在模板类中 -> 执行步骤2
在子类A中 -> 执行步骤 4
在模板类中 -> 执行步骤5
===========
在模板类中 -> 执行步骤1
在模板类中 -> 执行步骤2
在子类B中 -> 执行步骤 3
在模板类中 -> 执行步骤5
3. JdbcTemplate应用模板方法模式
在原生JDBC操作中,需要执行以下步骤:
- 获取connection
- 获取statement
- 获取resultset
- 遍历resultset并封装成集合
- 依次关闭connection, statement, resultset,并考虑各种异常
上面步骤中大多数都是重复的、可复用的,只有在遍历ResultSet并封装成集合的步骤是可定制的。每张表都映射不同的Java bean,这部分代码是没有办法复用的,只能定制。
// 模板方法,用来执行 JDBC 操作,返回结果集或受影响的行数
protected <T> T execute(ConnectionCallback<T> action, boolean enforceReadOnly) throws DataAccessException {Assert.notNull(action, "Callback object must not be null");Connection con = DataSourceUtils.getConnection(obtainDataSource());try {boolean readOnly = enforceReadOnly || isReadOnly();// 设置是否为只读连接prepareConnection(con, readOnly);// 执行具体的 JDBC 操作,该方法为子类实现T result = action.doInConnection(con);// 提交事务DataSourceUtils.commitIfNecessary(con, getDataSource());// 返回结果集或受影响的行数return result;} catch (SQLException ex) {// 回滚事务DataSourceUtils.rollbackIfNecessary(con, getDataSource());throw translateException("Callback", getSql(action), ex);} finally {DataSourceUtils.releaseConnection(con, getDataSource());}
}// 执行给定的 SQL 语句和参数,返回查询结果
public <T> T query(final String sql, final ResultSetExtractor<T> rse, Object... args) throws DataAccessException {Assert.notNull(sql, "SQL must not be null");Assert.notNull(rse, "ResultSetExtractor must not be null");// 匿名内部类,实现 ConnectionCallback 接口return execute(new ConnectionCallback<T>() {@Overridepublic T doInConnection(Connection con) throws SQLException {PreparedStatement ps = null;ResultSet rs = null;try {// 创建 PreparedStatement 对象ps = createPreparedStatement(con, sql);// 设置 PreparedStatement 的参数setValues(ps, args);// 执行查询,返回结果集rs = ps.executeQuery();// 对结果集进行处理,返回查询结果return rse.extractData(rs);} finally {JdbcUtils.closeResultSet(rs);JdbcUtils.closeStatement(ps);}}}, true);
}
4. 运用模板方法手写简单版JdbcTemplate
- 定义
DefineJcbcTemplate自定义jdbcTemplate接口
public interface DefineJcbcTemplate {<T> T queryForObject(String sql, DefineRowMapper<T> defineRowMapper);
}
- 定义
DefineRowMapper函数式接口,回调作用,处理jdbc查询结果ResultSet,返回泛型T对象
@FunctionalInterface
public interface DefineRowMapper<T> {T mapRow(ResultSet rs) throws SQLException;
}
DefineJcbcTemplateImpl实现类,该实现类的连接数据库、释放资源等操作都是从步骤一复制粘贴进来的,唯一不同的是把处理结果解耦出来了,定义好处理结果的接口DefineRowMapper,交由调用者去实现对结果的处理,最后回调该接口的方法mapRow并返回结果,其他的把连接数据库、释放资源封装起来,这样一来不用每次进行数据库查询都需要连接数据库、释放资源。
@Service
public class DefineJcbcTemplateImpl implements DefineJcbcTemplate {@Autowiredprivate DataSource dataSource;@Overridepublic <T> T queryForObject(String sql, DefineRowMapper<T> defineRowMapper) {// 一部分是准备和释放资源以及执行 SQL 语句,另一部分则是处理 SQL 执行结果Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;try {// 创建dataSource,获取连接connection = dataSource.getConnection();// 执行查询preparedStatement = connection.prepareStatement(sql);// 获取执行结果resultSet = preparedStatement.executeQuery();// 交由调用者去实现对结果的处理return defineRowMapper.mapRow(resultSet);} catch (Exception e) {e.printStackTrace();} finally {// 关闭资源if (preparedStatement != null) {try {preparedStatement.close();} catch (SQLException e) {e.printStackTrace();}}if (connection != null) {try {connection.close();} catch (SQLException e) {e.printStackTrace();}}}return null;}
}
- 调用自定义模板方法进行数据库查询
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestSpringTemplateApplication.class)
public class TestSpringTemplateApplicationTests {@Autowiredprivate DefineJcbcTemplate defineJcbcTemplate;@Testpublic void testQueryForObject() {// 定义sqlString sql = "SELECT * FROM user";// 通过模版方法进行查询List<User> users = defineJcbcTemplate.queryForObject(sql, (rs) -> {List<User> result = new ArrayList<>();while (rs.next()) {// 遍历ResultSet并封装成集合User user = new User();user.setId(rs.getInt("id"));user.setName(rs.getString("name"));user.setEmail(rs.getString("email"));result.add(user);}return result;});System.out.println(users);}
}
通过上面的代码,您可以看到模板方法模式可以很容易地将重复的部分代码提取到父类中,而将可变的部分代码放入子类中,从而实现代码的复用和扩展。
相关文章:
Spring源码中的模板方法模式
1. 什么是模板方法模式 模板方法模式(Template Method Pattern)是一种行为设计模式,它在操作中定义算法的框架,将一些步骤推迟到子类中。模板方法让子类在不改变算法结构的情况下重新定义算法的某些步骤。 模板方法模式的定义&…...
初学SpringMVC之 JSON 篇
JSON(JavaScript Object Notation,JS 对象标记)是一种轻量级的数据交换格式 采用完全独立于编程语言的文本格式来存储和表示数据 JSON 键值对是用来保存 JavaScript 对象的一种方式 比如:{"name": "张三"}…...
Mojo AI编程语言(三)数据结构:高效数据处理
目录 1. Mojo AI编程语言简介 2. 数据结构在数据处理中的重要性 3. Mojo AI中的基础数据结构 3.1 数组 3.2 列表 3.3 字典 4. 高效数据结构的实现与优化 4.1 哈希表 4.2 树结构 4.3 图结构 5. 高效数据处理技术 5.1 并行处理 5.2 内存优化 5.3 数据压缩 6. 实战…...
Java学习笔记整理: 关于SpringBoot 2024/7/12;
SpringBoot springboot也是spring公司开发的一款框架。为了简化spring项目的初始化搭建的。 特点specialty: springboot的特点: 1) 自动配置 Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程&a…...
ASP.NET MVC Lock锁的测试
思路:我们让后台Thread.Sleep一段时间,来模拟一个耗时操作,而这个时间可以由前台提供。 我们开启两个或以上的页面,第一个耗时5秒(提交5000),第二个耗时1秒(提交1000)。 期望的测试结果: 不加Lock锁&…...
Hadoop3:HDFS-通过配置黑白名单对集群进行扩缩容,并实现数据均衡(实用)
一、集群情况介绍 我的本地虚拟机,一共有三个节点,hadoop102、hadoop103、hadoop104 二、白名单 创建白名单文件whitelist,通过白名单的配置,只允许集群包含102和103两台机器可以存储数据,104无法存储数据。 需求 …...
TensorFlow系列:第五讲:移动端部署模型
项目地址:https://github.com/LionJackson/imageClassification Flutter项目地址:https://github.com/LionJackson/flutter_image 一. 模型转换 编写tflite模型工具类: import osimport PIL import tensorflow as tf import keras import …...
深度学习DeepLearning二元分类 学习笔记
文章目录 类别区分变量与概念逻辑回归Sigmoid函数公式决策边逻辑损失函数和代价函数逻辑回归的梯度下降泛化过拟合的解决方案正则化 类别区分 变量与概念 决策边置信度阈值threshold过拟合欠拟合正则化高偏差lambda(λ) 线性回归受个别极端值影响&…...
Eureka 介绍与使用
Eureka 是一个开源的服务发现框架,它主要用于在分布式系统中管理和发现服务实例。它由 Netflix 开发并开源,是 Netflix OSS 中的一部分。 使用 Eureka 可以方便地将新的服务实例注册到 Eureka 服务器,并且让其他服务通过 Eureka 服务器来发现…...
Java异常体系、UncaughtExceptionHandler、Spring MVC统一异常处理、Spring Boot统一异常处理
概述 所有异常都是继承自java.lang.Throwable类,Throwable有两个直接子类,Error和Exception。 Error用来表示程序底层或硬件有关的错误,这种错误和程序本身无关,如常见的NoClassDefFoundError。这种异常和程序本身无关࿰…...
bash终端快捷键
快捷键作用ShiftCtrlC复制ShiftCtrlV粘贴CtrlAltT新建终端ShiftPgUp/PgDn终端上下翻页滚动CtrlC终止命令CtrlD关闭终端CtrlA光标移动到最开始为止CtrlE光标移动到最末尾CtrlK删除此处到末尾的所有内容CtrlU删除此处至开始的所有内容CtrlD删除当前字符CtrlH删除当前字符的前一个…...
【Visual Studio】Visual Studio报错合集及解决办法
目录 Visual Studio报错:error LNK2001 Visual Studio报错:error C2061 Visual Studio报错:error C1075 Visual Studio报错:error C4430 Visual Studio报错error C3867 概述 持续更细Visual Studio报错及解决方法 Visual Studio报错:error LNK2001 问题 : error LNK2001…...
【微信小程序知识点】转发功能的实现
转发功能,主要帮助用户更流畅地与好友分享内容与服务。 想实现转发功能,有两种方式: 1.页面js文件必须声明onShareAppMessage事件监听函数,并自定义转发内容。只有定义了此事件处理函数,右上角菜单才会显示“转发”按…...
用python识别二维码(python实例二十三)
目录 1.认识Python 2.环境与工具 2.1 python环境 2.2 Visual Studio Code编译 3.识别二维码 3.1 代码构思 3.2 代码实例 3.3 运行结果 4.总结 1.认识Python Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可读性&…...
电脑文件夹怎么设置密码?让你的文件更安全!
在日常使用电脑的过程中,我们常常会有一些需要保护的个人文件或资料。为了防止这些文件被他人未经授权访问,对重要文件夹设置密码是一种有效的保护措施,可是电脑文件夹怎么设置密码呢?本文将介绍2种简单有效的方法帮助您为电脑文件…...
paddla模型转gguf
在使用ollama配置本地模型时,只支持gguf格式的模型,所以我们首先需要把自己的模型转化为bin格式,本文为paddle,onnx,pytorch格式的模型提供说明,safetensors格式比较简单请参考官方文档,或其它教…...
Memcached vs Redis——Java项目缓存选择
在Java项目开发中,缓存系统作为提升性能、优化资源利用的关键技术之一,扮演着至关重要的角色。Memcached和Redis作为两种流行的缓存解决方案,各有其独特的优势和应用场景。本文旨在通过分析项目大小、用户访问量、业务复杂度以及服务器部署情…...
大模型最新黑书:基于GPT-3、ChatGPT、GPT-4等Transformer架构的自然语言处理 PDF
今天给大家推荐一本丹尼斯罗斯曼(Denis Rothman)编写的关于大语言模型(LLM)权威教程<<大模型应用解决方案> 基于GPT-3、ChatGPT、GPT-4等Transformer架构的自然语言处理>!Google工程总监Antonio Gulli作序,这含金量不…...
【电子数据取证】电子数据司法鉴定
文章关键词:电子数据取证、司法鉴定服务、司法鉴定流程 一、定义 什么是司法鉴定? 在诉讼活动中鉴定人运用科学技术或者专业知识对诉讼涉及的专门性问题进行鉴别和判断并提供鉴定意见的活动。 电子数据司法鉴定 那么电子数据司法鉴定,就…...
使用 OpenCV 的 inRange 函数进行颜色分割
使用 OpenCV 的 inRange 函数进行颜色分割 在图像处理领域,颜色分割是一个常见的任务,常用于识别和提取图像中的特定颜色区域。OpenCV 提供了一个非常方便的函数 inRange 来实现这一功能。在这篇博客中,我们将详细介绍 inRange 函数的用法&a…...
探索AI辅助开发新范式:让快马平台成为你的专属前端智囊
最近在做一个需要收集用户反馈的小项目,发现用传统的表单方式实在太死板了。正好看到InsCode(快马)平台的AI辅助开发功能,决定试试用AI生成一个交互式反馈墙。没想到整个过程出奇地顺利,这里分享一下我的实践心得。 需求分析阶段 我首先在平…...
避开这5个坑,你的YOLO模型训练效率翻倍:从yaml配置到GPU显存优化实战
YOLO模型训练效率翻倍的5个关键避坑指南:从参数调优到显存管理实战 当你第一次用YOLOv10或v11跑通训练流程时,可能会觉得"不过如此"。但真正投入实战后,90%的开发者都会遇到显存爆炸、训练龟速、指标波动三大噩梦。上周有位使用RTX…...
提升物业服务满意度的物业管理小程序
一、首页核心服务入口基础功能模块:物业缴费、我的房产、通知公告、投诉建议、维修申报、小区活动、家政服务、优惠好物,覆盖业主日常高频需求信息与活动展示:顶部搜索栏:支持关键词检索,快速定位所需服务物业公告&…...
python异常模拟工具类(异常生成工具类)
文章目录创建代码类使用主要是做测试的时候方便,创建代码类 1、新建python文件exception_mock_utils.py,代码为: import random import time from typing import Any, Optionalclass ExceptionMockUtils:"""异常模拟工具类用…...
实体店有没有必要做门店小程序?
在当前消费行为不断向线上延伸的背景下,实体店是否需要搭建门店小程序,已经成为很多经营者在数字化转型过程中必须面对的问题。实体店是否有必要做门店小程序,取决于其是否需要提升获客能力与用户复购效率。一、为什么会出现这个问题在实际经…...
nlp_structbert_sentence-similarity_chinese-large入门指南:从ModelScope下载到本地Web服务上线
nlp_structbert_sentence-similarity_chinese-large入门指南:从ModelScope下载到本地Web服务上线 你是不是经常需要判断两句话是不是一个意思?比如,检查用户提问是不是同一个问题,或者看看两段文案是不是在说同一件事。以前做这种…...
数字创世神:用漏洞规律操控现实
在古老的神话中,数字“一”象征着万物的起源与开端,是混沌初开、宇宙诞生的起点。伏羲一画开天,划分乾坤,自此有了天地与秩序。这种从无到有、从一到多的创世过程,与当今数字世界的构建有着惊人的同构性。在由代码构筑…...
Linux信号机制:原理、处理与实践
1. Linux信号机制基础解析在Linux系统中,信号是一种进程间通信的重要机制。想象一下你正在厨房做饭,突然门铃响了——这个门铃就相当于Linux系统中的信号,它打断了你当前的工作流程,迫使你做出响应。信号本质上是一种异步事件通知…...
在Jetson Orin Nano上手动编译部署AirSLAM:如何解决TensorRT模型转换(ONNX转Engine)的内存溢出问题
在Jetson Orin Nano上手动编译部署AirSLAM:解决TensorRT模型转换内存溢出的实战指南 1. 边缘设备部署AirSLAM的核心挑战 Jetson Orin Nano作为NVIDIA面向边缘计算推出的高性能模块,其4GB/8GB内存配置在运行复杂视觉SLAM算法时面临严峻的资源约束。AirSLA…...
Alibaba DASD-4B Thinking 对话工具在网络安全领域的应用:智能威胁分析与响应
Alibaba DASD-4B Thinking 对话工具在网络安全领域的应用:智能威胁分析与响应 每天,安全运维团队的工程师们都要面对海量的安全告警。防火墙日志、入侵检测系统的报警、终端防护软件的提示……这些信息像潮水一样涌来。传统的处理方式,往往依…...
