MyBatis源码中的设计模式1

1. 建造者模式的应用

建造者模式属于创建类模式,通过一步一步地创建一个复杂的对象,能够将部件与其组装过程分开。用户只需指定复杂对象的类型,就可以得到该对象,而不需要了解其内部的具体构造细节。《Effective Java》中也提到,遇到多个构造器参数时,考虑用构建者(Builder)模式。

在 Mybatis 的环境初始化过程中,SqlSessionFactoryBuilder会调用XMLConfigBuilder读取所有的MybatisMapConfig.xml和所有的*Mapper.xml文件,构建 Mybatis 运行的核心对象Configuration对象,然后将该Configuration对象作为参数构建一个SqlSessionFactory对象。

示例图

在这里插入图片描述

其中,XMLConfigBuilder在构建Configuration对象时,也会调用XMLMapperBuilder用于读取*.Mapper文件,而XMLMapperBuilder会使用XMLStatementBuilder来读取和构建所有的 SQL 语句。

示例图

在这里插入图片描述

在这个过程中,Builder模式会读取文件或者配置,然后做大量的 XPath 解析、配置或语法解析、反射生成对象、存入结果缓存等步骤。因此,大量采用了 Builder 模式来解决这些问题。

对于Builder的具体类,方法大都用build*开头,比如SqlSessionFactoryBuilder类中包含的方法:

示例图

在这里插入图片描述

从建造者模式的设计初衷来看,SqlSessionFactoryBuilder虽然带有 Builder 后缀,但不完全是标准的建造者模式。它的设计初衷是为了简化开发,隐藏构建SqlSessionFactory的复杂过程,对程序员透明。

2. 工厂模式的应用

在 Mybatis 中,SqlSessionFactory使用了简单工厂模式。

简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。简单工厂模式中,可以根据参数的不同返回不同类的实例。

示例图

在这里插入图片描述

SqlSession是 Mybatis 工作的核心接口,通过这个接口可以执行 SQL 语句、获取 Mappers、管理事务。

示例图

在这里插入图片描述

DefaultSqlSessionFactory的默认工厂实现里,openSessionFromDataSource方法展示了工厂如何产出一个产品:

示例图

在这里插入图片描述

这个方法会先从configuration读取对应的环境配置,然后初始化TransactionFactory获得一个Transaction对象,通过Transaction获取一个Executor对象,最后通过configurationExecutorautoCommit参数构建了SqlSession

3. 代理模式的应用

代理模式是 Mybatis 核心使用的模式,使我们只需要编写Mapper.java接口,不需要实现,由 Mybatis 背后完成具体 SQL 的执行。

代理模式(Proxy Pattern):给某个对象提供一个代理,并由代理对象控制对原对象的引用。

示例图

在这里插入图片描述

每次调用sqlSessiongetMapper方法时,都会创建一个新的动态代理类实例。

示例图

在这里插入图片描述

当我们使用ConfigurationgetMapper方法时,会调用mapperRegistry.getMapper方法,

示例图

在这里插入图片描述

在这里,通过T newInstance(SqlSession sqlSession)方法得到一个MapperProxy对象,然后调用T newInstance(MapperProxy<T> mapperProxy)生成代理对象。

示例图

在这里插入图片描述

通过这种方式,我们只需要编写Mapper.java接口类,实际执行时会转发给MapperProxy.invoke方法,调用后续的sqlSession.cud > executor.execute > prepareStatement等方法,完成 SQL 的执行和返回。

4. 模板方法模式的应用

在 Mybatis 中,sqlSession的 SQL 执行委托给Executor实现,Executor包含以下结构:

示例图

在这里插入图片描述

其中的BaseExecutor采用了模板方法模式,实现了大部分的 SQL 执行逻辑,把几个方法交给子类定制化完成。

示例图

在这里插入图片描述

模板模式基于继承实现代码复用。抽象类中包含模板方法,调用有待子类实现的抽象方法。

5. 装饰者模式的应用

装饰模式(Decorator Pattern):动态地给一个对象增加一些额外的职责,比生成子类实现更为灵活。

在 Mybatis 中,缓存功能由根接口Cache定义,采用装饰器设计模式,数据存储和缓存的基本功能由PerpetualCache实现,然后通过一系列的装饰器来对PerpetualCache进行缓存策略等方面的控制。

示例图

在这里插入图片描述

用于装饰PerpetualCache的标准装饰器有:

  1. FifoCache
  2. LoggingCache
  3. LruCache
  4. ScheduledCache
  5. SerializedCache
  6. SoftCache
  7. SynchronizedCache
  8. WeakCache

Mybatis 采用装饰器模式实现缓存功能,通过组合而非继承,更加灵活,避免了继承关系的组合爆炸。

6. 迭代器模式的应用

迭代器模式介绍

  • 迭代器模式是一个行为型设计模式,用于在不暴露其底层表示的情况下顺序访问集合对象的元素。在大多数编程语言中,迭代器已经成为基础的类库,直接用来遍历集合对象。在日常开发中,我们通常直接使用现有的迭代器,而不需要从零实现一个。
  • 在软件系统中,容器对象有两个职责:存储数据和遍历数据。从依赖性角度看,前者是聚合对象的基本职责,后者是可变化且可分离的。因此,可以将遍历数据的行为从容器中抽取出来,封装到迭代器对象中,由迭代器提供遍历数据的功能。这将简化聚合对象的设计,更加符合单一职责原则。

在这里插入图片描述

迭代器模式主要包含以下角色:

  1. 抽象集合(Aggregate)角色:用于存储和管理元素对象,定义存储、添加、删除集合元素的功能,并声明一个 createIterator() 方法用于创建迭代器对象。
  2. 具体集合(ConcreteAggregate)角色:实现抽象集合类,返回一个具体迭代器的实例。
  3. 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()next() 等方法。
    • hasNext() 方法用于判断集合中是否还有下一个元素。
    • next() 方法用于将游标后移一位元素。
    • currentItem() 方法用于返回当前游标指向的元素。
  4. 具体迭代器(ConcreteIterator)角色:实现抽象迭代器接口中定义的方法,完成对集合对象的遍历,同时记录遍历的当前位置。

在Java中,Iterator接口就是迭代器模式的实现,只要实现了该接口,就相当于应用了迭代器模式:

在这里插入图片描述

迭代器模式总结

使用场景

  1. 访问一个聚合对象的内容,不需要暴露它的内部表示。
  2. 支持对聚合对象的多种遍历。
  3. 迭代器模式与集合同时存在。

优点

  1. 支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。
  2. 迭代器简化了聚合类。引入迭代器模式后,聚合对象不再需要自行提供数据遍历访问的方法。
  3. 可以为不同的聚合结构提供一个统一的接口。

缺点

  1. 迭代器模式将存储数据和遍历数据的职责分离开,增加新的聚合类型需要增加对应的新迭代器类,增加了系统复杂性。

MyBatis中的应用

MyBatis的 PropertyTokenizer 是 property 包中的重要类,它实现了 Iterator 接口,并在 reflection 包中的其他类中被频繁引用。该类的 hasNext() 方法经常被使用。

/**
 * 属性分词器
 * 
 * 实现 Iterator 接口,用于遍历属性的各个部分
 */
public class PropertyTokenizer implements Iterator<PropertyTokenizer> {

    private String name;
    private final String indexedName;
    private String index;
    private final String children;

    public PropertyTokenizer(String fullname) {
        int delim = fullname.indexOf('.');
        if (delim > -1) {
            name = fullname.substring(0, delim);
            children = fullname.substring(delim + 1);
        } else {
            name = fullname;
            children = null;
        }
        indexedName = name;
        delim = name.indexOf('[');
        if (delim > -1) {
            index = name.substring(delim + 1, name.length() - 1);
            name = name.substring(0, delim);
        }
    }

    public String getName() {
        return name;
    }

    public String getIndex() {
        return index;
    }

    public String getIndexedName() {
        return indexedName;
    }

    public String getChildren() {
        return children;
    }

    @Override
    public boolean hasNext() {
        return children != null;
    }

    @Override
    public PropertyTokenizer next() {
        return new PropertyTokenizer(children);
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException("Remove is not supported, as it has no meaning in the context of properties.");
    }
}

这个类传入一个字符串到构造函数,然后提供了 iterator 方法对解析后的子串进行遍历,是一个非常常用的方法类。

PropertyTokenizer 类虽然实现了 Iterator 接口,但并非标准的迭代器类。它将配置解析、解析后的元素、迭代器这三部分本应分开的代码耦合在一起,因此略显复杂。不过,这样做的好处是能够实现惰性解析,不需要事先将整个配置解析成多个 PropertyTokenizer 对象,只有在调用 next() 方法时才会解析部分配置。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/801612.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CH552G使用IAP下载

常见下载中的方式ISP&#xff0c;IAP&#xff0c;ICP 参考&#xff0c;CH552G中文手册&#xff0c;参考1 ISP&#xff1a;In System Programing&#xff0c;在系统编程。是常见的&#xff0c;使用软件&#xff0c;先将某个引脚&#xff08;例如boot&#xff09;连接到合适的电…

领航Linux UDP:构建高效网络新纪元

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 文章目录 引言Udp和Tcp的异同相同点不同点总结 1.1、socket1.2、bind1.3、recvfrom1.4、sendto2.1、代码2.1、说明3.1、代码3.2、说明 引言 在前几篇博客中&#xff0c;我们学习了Linux网络编程中的一些概念。…

【Django】网上蛋糕项目商城-购物车和我的订单功能

1.购物车功能 在首页中的滚动栏的商品&#xff0c;热门商品&#xff0c;新品&#xff0c;以及商品详情中都有加入购物车按钮 在models文件中创建购物车表&#xff0c;用于保存当前用户添加的商品信息 # 购物车表 class ShoppingCar(models.Model):# 用户iduserIdmodels.Integ…

JRT打印设计器解耦

为了让打印设计器可以给多个产品打印通用&#xff0c;那么设计器就不能嵌入太多具体产品的业务信息。比如医院主键、工作组、医嘱关联登。 设计器在设计表的时候就没引入检验部分的依赖&#xff0c;采用产品组唯一标识和产品组业务ID来隔离不同组的模板设计。 维护菜单时候就…

CTFshow--web--xss

目录 web316 web317~319 web320~326 web327 web328 web329 web330 web331 web332 web333 先在自己的服务器写上代码 <?php$content $_GET[1]; if(isset($content)){file_put_contents(flag.txt,$content); }else{echo no data input; }要拿到管理员的cookie , 而…

Java - 程序员面试笔记记录 实现 - Part5

7.1 Struts 优点&#xff1a; 1. MVC模式实现了表现与逻辑的分离&#xff0c;扩展性高。 2. 提供页面导航功能&#xff0c;通过配置文件建立整个系统各部分之间的联系。 3. 集成了一些常用处理功能。 缺点&#xff1a; 1. 仅面向 Web 应用程序开发 2. Action 非线程安全…

HTML+CSS+JS井字棋(来自动下棋)

井字棋 自动下棋 玩家先下&#xff0c;计算机后下 源码在图片后面 点赞❤️收藏⭐️关注&#x1f60d; 效果图 源代码 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <title>Tic Tac Toe Game</tit…

开始Linux之路

人生得一知己足矣&#xff0c;斯世当以同怀视之。——鲁迅 Linux操作系统简单操作指令 1、ls指令2、pwd命令3、cd指令4、mkdir指令(重要)5、whoami命令6、创建一个普通用户7、重新认识指令8、which指令9、alias命令10、touch指令11、rmdir指令 及 rm指令(重要)12、man指令(重要…

技术周总结 2024.07.08~07.14(算法,Python,Java,Scala,PHP)

文章目录 一、07.13 周六1.0&#xff09;算法题&#xff1a;字符串中的单词反转1.1&#xff09; 问题01:可靠性计算中的MTTR MTTF MTBF 分别指什么&#xff1f;他们之间有什么联系&#xff1f;MTTR (Mean Time to Repair)MTTF (Mean Time to Failure)MTBF (Mean Time Between F…

韦东山嵌入式linux系列-驱动进化之路:总线设备驱动模型-课后作业

在内核源码中搜索 platform_device_register 可以得到很多驱动&#xff0c;选择 一个作为例子&#xff1a; ① 确定它的名字 ② 根据它的名字找到对应的 platform_driver ③ 进入 platform_device_register/platform_driver_register 内部&#xff0c;分析 dev 和 drv 的匹配过…

某林操作系统——网络系统

什么是零拷贝&#xff1f; DMA技术&#xff1a;使用一个DMA控制器将数据从硬盘传输到内存&#xff0c;除了一开始调用DMA控制器发起传输&#xff0c;数据搬运全称不需要CPU参与。 发送一段数据到网上如下所示 4次状态切换4次拷贝。 为了加快速度就要减少上下文切换&#xff…

网络安全防御【防火墙安全策略用户认证综合实验】

目录 一、实验拓扑图 二、实验要求 三、实验思路 四、实验步骤 1、打开ensp防火墙的web服务&#xff08;带内管理的工作模式&#xff09; 2、在FW1的web网页中网络相关配置 3、交换机LSW6&#xff08;总公司&#xff09;的相关配置&#xff1a; 4、路由器相关接口配置&a…

【深度学习入门篇 ⑦】PyTorch池化层

【&#x1f34a;易编橙&#xff1a;一个帮助编程小伙伴少走弯路的终身成长社群&#x1f34a;】 大家好&#xff0c;我是小森( &#xfe61;ˆoˆ&#xfe61; ) &#xff01; 易编橙终身成长社群创始团队嘉宾&#xff0c;橙似锦计划领衔成员、阿里云专家博主、腾讯云内容共创官…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【查询密钥是否存在(ArkTS)】

查询密钥是否存在(ArkTS) HUKS提供了接口供应用查询指定密钥是否存在。 开发步骤 指定密钥别名keyAlias&#xff0c;密钥别名最大长度为64字节。初始化密钥属性集。用于查询时指定密钥的属性TAG&#xff0c;比如查询的密钥范围(全量/单个)&#xff0c;当查询单个时&#xff…

工时计算软件比较:哪些功能是必需的?

国内外主流的10款工时计算软件对比&#xff1a;PingCode、Worktile、蓝凌OA、用友软件、金蝶软件、智能人事、Time Doctor、Toggl Track、Clockify、Harvest。 在选择合适的工时计算软件时&#xff0c;很多企业面临的一个主要痛点是如何找到既能满足功能需求又具有成本效益的解…

虚拟机及其Debian(kali)安装

本机电脑为Windows10系统专业版&#xff0c;在此基础上安装VMware和系统&#xff08;Kali&#xff09; 步骤如下 一、安装 VMware Workstation Pro v16.2.4 安装步骤可参照网上博客&#xff0c;该步骤较简单&#xff0c;此处不做讲解。文件中共计两个&#xff0c;其中一个是激活…

YOLOv8白皮书-第Y8周:yolov8.yaml文件解读

本文为365天深度学习训练营中的学习记录博客 原作者&#xff1a;K同学啊|接辅导、项目定制 请根据YOLOv8n、YOLOv8s模型的结构输出&#xff0c;手写出YOLOv8l的模型输出 文件位置&#xff1a;./ultralytics/cfg/models/v8/yolov8.yaml 一、参数配置 # Parameters nc: 80 # n…

android APP在蓝牙模拟键盘扫描条码设备开机时闪退

&#x1f3c6;本文收录于《CSDN问答解答》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&…

下载安装VSCode并添加插件作为仓颉编程入门编辑器

VSCode下载地址&#xff1a;下载 Visual Studio Code - Mac、Linux、Windows 插件下载&#xff1a;GitCode - 全球开发者的开源社区,开源代码托管平台 仓颉社区中下载解压 cangjie.vsix 插件 打开VSCode 按 Ctrl Shift X 弹出下图 按照上图步骤依次点击选中我们下…

【Unity2D 2022:UI】制作主菜单

一、创建主菜单游戏场景 1. 在Scenes文件夹中新建一个游戏场景Main Menu 2. 为场景添加背景 &#xff08;1&#xff09;创建画布Canvas &#xff08;2&#xff09;在Canvas中创建新的空游戏物体Main Menu &#xff08;3&#xff09;在Main Menu中新建一个图像游戏物体Backgrou…