[系列目录]
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开发者知识点