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…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...

视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
tomcat指定使用的jdk版本
说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...

沙箱虚拟化技术虚拟机容器之间的关系详解
问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西,但是如果把三者放在一起,它们之间到底什么关系?又有什么联系呢?我不是很明白!!! 就比如说: 沙箱&#…...