【系列目录】
Mybatis源码阅读之一——工厂模式与SqlSessionFactory
Mybatis源码阅读之二——模板方法模式与Executor
Mybatis源码阅读之三——JDBC解析与Mybatis封装
Mybatis源码阅读之四——装饰器模式与Mybatis中的各种Cache
Mybatis源码阅读之六——数据库连接池实现与hikariCP简析
【本文目录】
门面模式
什么是门面模式?
门面模式,又称外观模式,该模式提供一组统一的接口,让子系统更加易用。
门面模式用类图不好表示,一个大致流程图如下:
当一个外部调用到来时,如果调用的时方法A,那么将被Facade转发给A类去执行;如果是方法B/C则会分别转发给B/C类去执行。
由此可以看出,门面模式实际上使用一个门面类统合了琐碎的子类,让外部调用的使用方不需要再认知底层有这些ABC类,只需要与门面类打交道,实现了很好的解耦。
从上面的概念中,我们会发现,这个模式其实在我们不知不觉中经常会使用。
对于Java Web开发者来说,一些简单的domain是从低向上,从mapper–service–controller去设计,比如domain是商品,那么就是mapper<商品>–service<商品>–controller<商品>。
但是也会有很多复杂场景是从上向下的,典型的比如LoginController,我们可以将登陆/登出/当前权限获取/当前用户获取等都放在这个里面,进行一个统一管理,而具体方法的实现则可能是转发到了各个不同的Service中去。
SqlSession中的门面模式
回顾我们之前在Mybatis源码阅读之一——工厂模式与SqlSessionFactory中介绍的SqlSession,这就是一个典型的门面模式的实现。
其中CRUD内部是对Executor的转发,而getMapper/getConfiguration则是configuration的转发。
bind模块
包结构
binding包下的类虽然不多,却是核心逻辑的一部分。
- MapperRegistry : Mapper的主要管理者,上述sqlSession的getMapper方法,最终就是转发到了这里。
- MapperProxy : 我们业务mapper的xml生成的代理对象,(后面与代理模式一起分享)
- MapperMethod : 对mapper方法级别的封装。
源码
回顾我们第一章的demo(下图),其中
这个demo中关于bing的部分,分两步来看:
- 如图1,环境加载过程中的binding mapper相关逻辑。
- 如图2,业务系统获取bind Mapper的过程。
Binging Mapper
-
SqlSessionFactoryBuilder.build
-
SqlSessionFactory.build(parser.parse())
这里分两步看,一个是build让SqlSessionFactory持有了configuration对象。另一个则是对继续对document的解析(不了解document的可以看Mybatis源码阅读之五——Java的XML解析)。
-
XMLConfigBuilder.parse()
-
XMLConfigBuilder.parseConfiguration,这里我们只关注对mapper的处理逻辑。
-
重要XMLConfigBuilder.mapperElement()
先来看一个mybatis-config.xml中配置mapper的两种方式,一个是mapper标签配置到类,另一种是package标签配置到包。
回到代码:
- 第一步,对mappers下的标签循环处理。
- 第二步,判断当前是package标签,是的话addMappers,这里稍后展开。
- 第三步,如果不是package,那就是mapper标签。mapper中也分三种case,第一种是使用resource属性,此方式可以加载本地任意位置的xml文件。
- 第四步,接第三步,使用url属性,此方式可以加载本地/网络任意url的文件,第三四补都是直接拿到了xml资源文件,故而此处是直接进行了xml文件解析。
- 第五步,接第三步,使用class属性,此方式指定的是Mapper类,同时对应的xml必须同名,此处同package方式类似逻辑,不是解析xml资源,而是调用configuration进行addMapper。
-
configuration.addMappers,如图,此时转发给了binding包下的MapperRegistry,MapperRegistry是在Configuration实例化时饿汉式单例加载的。
-
MapperRegistry.addMappers
-
MapperRegistry.addMapper
-
MapperAnnotationBuilder.parse
-
值得一提,parseCache方法解析了一个注解,CacheNamespace,使用此注解也可以代替在xml中配置的方式来指定此Mapper使用二级缓存,源码与案例如下:
此处可以使用指定的缓存,如果我们有LFU或者其他类型的缓存需求,可以自定义实现。
-
回到MapperAnnotationBuilder.loadXmlResource(),
此处就会将package方式与class方式加载的mapper对应的xml资源加载,而加载的文件名就是Mapper的文件名。
getMapper流程
现在Mappers都已经被Mybatis管理起来了,存放在MapperRegistry.knownMappers中。
回顾demo,getMapper时:
- SqlSession.getMapper
- Configuration.getMapper
- MapperRegistry.getMapper
这里发现对于每一个sqlSession来说会生成一个Mapper代理,不是我们平常理解的一个Mapper全局只有一个Mapper代理,这种设计直接将Mapper代理与SqlSession绑定在了一起。
- MapperProxy.invoke
这里很值得一提,具体的方法执行器有两个,DefaultMethodInvoker用于处理接口中的default方法,PlainMethodInvoker用于处理真正的xmlMapper方法(即sql)
。
- DefaultMethodInvoker.invoke
直接使用了jdk反射提供的MethodHandle来执行defaulr方法。 - PlainMethodInvoker.invoke()
这里才是拿到sql,真正去执行的地方。
关于代理模式,我们放在下次与Mybatis插件的实现一起解读。
总结
本文我们介绍了门面模式以及Mybatis中的应用,了解了一个Mapper以及他的xml资源文件何时/如何被加载,也了解了getMapper的简单过程。
额外的,还有配置mapper时,四种配置方式,package,resource,url,class。
以及了解二级缓存CacheNamespace。
欢迎关注微信公众号 【JAVA技术分享官】,公众号首发,持续输出原创高质量JAVA开发者知识点