Mybatis源码阅读之二——模板方法模式与Executor

[系列目录]
Mybatis源码阅读之一——工厂模式与SqlSessionFactory


Mybatis中的Executor应用了模板方法模式,我们有必要先来了解这一种行为型设计模式。

一. 模板方法模式

行为型模式是对在不同的对象之间划分责任和算法的抽象化。
通过行为型模式,可以更加清晰地划分类与对象的职责,并研究系统在运行时实例对象 之间的交互。

模板方法模式通过继承关系在父类与子类之间分配行为,父类负责通用逻辑的实现,不同的逻辑则是提供统一的方法,交由子类分别继承实现。

  • 实现一
    BaseService抽象类实现了doAction01方法,doAction02方法为abstract或者内部实现为直接抛异常。

  • 实现二
    使用java提供的default关键字,用接口替换抽象类来使用。

总结: 通过如上两种实现方式,可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些
步骤。这种模式在我们有大量重复逻辑的,不同类型又有些区别差异的时候显然是十分适合的。

比如前文【Mybatis源码阅读之一——工厂模式与SqlSessionFactory】有提到的采购单与销售单各自的处理逻辑有大量相同代码,这部分可以提取到模板类,而剩余有区别的逻辑提取出来,作为一个差异方法来各自实现。

二. 同步回调与匿名函数

同步回调能够做到与模板方法模式类似的事情,前者更加灵活。
借鉴【设计模式之美】中的例子,spring中的JdbcTemplate虽然名字上带了模板,但是其实内部实现使用的是同步回调。
如JdbcTemplate.query()方法,以及batchUpdate方法,最终都使用了execute()方法,而他们自身应有的逻辑则是作为回调传递给了execute(),实现了自身逻辑的抽离,使得execute()方法有了模板类的作用。

值得一提的是,JdbcTemplate使用的是回调类,如果我们自己做开发,能够精简成匿名函数的话,个人认为易读性会更好一些。
关于JAVA中匿名函数的使用可以参考这篇https://blog.csdn.net/qq_35946969/article/details/108492198

三. Executor

回到Mybatis源码部分,从类图看,CachingExecutor除外,Executor->BaseExecutor->各个实现类,这个结构是一个经典的模板方法模式,比我们上述部分中,抽象类的上方多了一层接口。

先看一下Executor的接口的各方法以及大致功能描述:

这些方法我们分开说,先来看CRUD。

BaseExecutor与其子类

  • BaseExecutor.update()

    当我们想要继续找doUpdate时,却发现这是一个抽象接口,这就是模板方法的具体应用,doUpdate的实现交给了子类,看一下他们各自的实现。
  • SimpleExecutor

    由这里面的方法可得知,BaseExecutor留给子类实现的方法并不多,主要是CRUD和doFlushStatements。对于SimpleExecutor的doUpdate来说做了简单两件事情。
    • 第一步,通过configuration生成StatementHandler,简单说一下,configuration是mybatis的全局配置类,几乎所有的配置项都放在这个类。
    • 第二步,使用JDBC(对应的数据库驱动)的Statement进行sql执行。这里不再深入,关于JDBC和Mybatis中的相关Handler内容过多,我们下一章再来详细分析
  • BatchExecutor
    顾名思义,是SimpleExecutor的批量版。
    • 第一步,如果是复用statement,只需要存储参数。
    • 第二步,如果是新的statement,存储下来。
    • 第三步,通过JDBC的Statement.addBatch()方法将statement存储,等待之后的批量提交。
  • ReuseExecutor
    是SimpleExecutor的可复用版,此类的关键就在statementMap这个全局变量,他会缓存sql->Statement的对应关系,逻辑简单不再展开。

一二级缓存与CacheExecutor

回到Executor的接口,我们继续往下看缓存相关的接口:
上面我们介绍了BaseExecutor的子类只有CRUD等几个少数方法,缓存相关的接口只在BaseExecutor/CacheExecutor实现。

  • CachingExecutor.createCacheKey()
    可以看到CachingExecutor什么都没做,直接转给了自己内部的Executor代理类,那么具体的实现只会在BaseExecutor中了。

    值得一提,这里的代理Executor体现了CacheExecutor的作用,他其实只是一个BaseExecutor的装饰器,在BaseExecutor的基础上,增加了二级缓存功能

  • BaseExecutor.createCacheKey()
    这里的实现其实就是将sql等一些信息组装起来,作为一个CacheKey对象返回,用于之后做一级缓存/二级缓存的key。

  • BaseExecutor.clearLocalCache()
    CachingExecutor中该方法仍然是直接调用代理Executor,我们直接看BaseExecutor。

    这里的localCache其实就是我们所说的一级缓存,PerpetualCache可以直接看作一个Map结构(Cache相关的具体涉及我们放到后面与装饰器一起解读

  • 一级缓存
    让我们再找找这个一级缓存被使用到的地方。

    • 查询时:
    • 从数据库查出时更新:
  • 二级缓存
    之前我们提到了二级缓存的实现就是CacheExecutor。
    查询时,会先经过二级缓存,找不到才会进入代理(原始)Executor。

总结

Executor与BaseExecutor采用了模板方法模式,扩展了批量操作,单个操作,复用操作等子类,同时又使用CacheExecutor装饰器模式做了二级缓存的实现。


欢迎关注微信公众号 【JAVA技术分享官】,公众号首发,持续输出原创高质量JAVA开发者知识点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值