MyBatis源码骨架分析
源码包分析
MyBatis 源码下载地址:https://github.com/MyBatis/MyBatis-3
MyBatis源码导入过程:
下载MyBatis的源码
检查maven的版本,必须是3.25以上,建议使用maven的最新版本
MyBatis的工程是maven工程,在开发工具中导入,工程必须使用jdk1.8以上版本;
把MyBatis源码的pom文件中<optional>true</optional>,全部改为false;
在工程目录下执行 mvn clean install -Dmaven.test.skip=true,将当前工程安装到本地仓库(pdf插件报错的话,需要将这个插件屏蔽); 注意:安装过程中会可能会有很多异常信息,只要不中断运行,请耐心等待;
其他工程依赖此工程
MyBatis整体架构

外观模式(门面模式)

门面模式:提供了一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,让子系统更容易使用。
优点
使复杂子系统的接口变的简单可用,减少了客户端对子系统的依赖,达到了解耦的效果;
遵循了OO原则中的迪米特法则,对内封装具体细节,对外只暴露必要的接口。
使用场景
一个复杂的模块或子系统提供一个供外界访问的接口
子系统相对独立 ― 外界对子系统的访问只要黑箱操作即可
谈谈设计模式的几个原则
单一职责原则:一个类或者一个接口只负责唯一项职责,尽量设计出功能单一的接口;
依赖倒转原则:高层模块不应该依赖低层模块具体实现,解耦高层与低层。既面向接口编程,当实现发生变化时,只需提供新的实现类,不需要修改高层模块代码;
开放-封闭原则:程序对外扩展开放,对修改关闭;换句话说,当需求发生变化时,我们可以通过添加新模块来满足新需求,而不是通过修改原来的实现代码来满足新需求;
迪米特法则:一个对象应该对其他对象保持最少的了解,尽量降低类与类之间的耦合度;
里氏代换原则:所有引用基类(父类)的地方必须能透明地使用其子类的对象;
接口隔离原则:客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上;
基础支撑层源码分析

基础支撑层源码分析 日志模块需求
MyBatis没有提供日志的实现类,需要接入第三方的日志组件,但第三方日志组件都有各自的Log级别,且各不相同,而MyBatis统一提供了trace、debug、warn、error四个级别;
自动扫描日志实现,并且第三方日志插件加载优先级如下:slf4J → commonsLoging → Log4J2 → Log4J → JdkLog;
日志的使用要优雅的嵌入到主体功能中;
适配器模式

Target:目标角色,期待得到的接口.
Adaptee:适配者角色,被适配的接口.
Adapter:适配器角色,将源接口转换成目标接口.
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁,将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作;
适用场景:当调用双方都不太容易修改的时候,为了复用现有组件可以使用适配器模式;在系统中接入第三方组件的时候经常被使用到;
注意:如果系统中存在过多的适配器,会增加系统的复杂性,设计人员应考虑对系统进行重构;
日志模块类图

代理模式
代理模式定义:
给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用;
目的:
(1)通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;
(2)通过代理对象对原有的业务增强;
代理模式调用示意图

在MyBatis中那些地方需要打印日志?
ConnectionLogger:负责打印连接信息和SQL语句,并创建PreparedStatementLogger;
PreparedStatementLogger:负责打印参数信息,并创建ResultSetLogger
ResultSetLogge:r负责打印数据结果信息;

基础支撑层源码分析 创建一个数据源的难点?
常见的数据源组件都实现了javax.sql.DataSource接口;
MyBatis不但要能集成第三方的数据源组件,自身也提供了数据源的实现;
一般情况下,数据源的初始化过程参数较多,比较复杂;
工厂模式uml类图
工厂模式(Factory Pattern)属于创建型模式,它提供了一种创建对象的最佳方式。定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行

形象化描述:

产品接口(Product):产品接口用于定义产品类的功能,具体工厂类产生的所有产品都必须实现这个接口。调用者与产品接口直接交互,这是调用者最关心的接口;
具体产品类(ConcreteProduct):实现产品接口的实现类,具体产品类中定义了具体的业务逻辑;
工厂接口(Factory):工厂接口是工厂方法模式的核心接口,调用者会直接和工厂接口交互用于获取具体的产品实现类;
具体工厂类(ConcreteFactory):是工厂接口的实现类,用于实例化产品对象,不同的具体工厂类会根据需求实例化不同的产品实现类;
数据源模块类图

为什么要使用工厂模式?

数据源连接池核心类
PooledDataSource:一个简单,同步的、线程安全的数据库连接池
PooledConnection:使用动态代理封装了真正的数据库连接对象;
PoolState:用于管理PooledConnection对象状态的组件,通过两个list分别 管理空闲状态的连接资源和活跃状态的连接资源
高频面试题:请详细描述从数据库连接池中获取一个连接资源的过程?
PooledDataSource 获取和归还连接过程

getConnection()

pushConnection()
基础支撑层源码分析 缓存模块需求
MyBatis缓存的实现是基于Map的,从缓存里面读写数据是缓存模块的核心基础功能;
除核心功能之外,有很多额外的附加功能,如:防止缓存击穿,添加缓存清空策略(fifo、lru)、序列化功能、日志能力、定时清空能力等;
附加功能可以以任意的组合附加到核心基础功能之上;
怎么样优雅的为核心功能添加多种附加能力?
使用动态代理或继承的办法扩展多种附加功能?
A:这些方式是静态的,用户不能控制增加行为的方式和时机。另外,新功能的存在多种组合,使用继承可能导致大量子类存在;
例子:
使用继承扩展王美丽?


装饰器模式uml类图
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,是一种用于代替继承的技术,无需通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀;

组件(Component):组件接口定义了全部组件类和装饰器实现的行为;
组件实现类(ConcreteComponent):实现Component接口,组件实现类就是被装饰器装饰的原始对象,新功能或者附加功能都是通过装饰器添加到该类的对象上的;
装饰器抽象类(Decorator):实现Component接口的抽象类,在其中封装了一个Component 对象,也就是被装饰的对象;
具体装饰器类(ConcreteDecorator):该实现类要向被装饰的对象添加某些功能;
装饰器模式使用图示

优点
相对于继承,装饰器模式灵活性更强,扩展性更强;
灵活性:装饰器模式将功能切分成一个个独立的装饰器,在运行期可以根据需要动态的添加功能,甚至对添加的新功能进行自由的组合;
扩展性:当有新功能要添加的时候,只需要添加新的装饰器实现类,然后通过组合方式添加这个新装饰器,无需修改已有代码,符合开闭原则;
装饰器模式使用举例
·IO中输入流和输出流的设计:
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("c://a.txt")));
·对网络爬虫的自定义增强,可增强的功能包括:多线程能力、缓存、自动生成报表、黑白名单、random触发等
·MyBatis的缓存组件

Cache:Cache接口是缓存模块的核心接口,定义了缓存的基本操作;
PerpetualCache:在缓存模块中扮演ConcreteComponent角色,使用HashMap来实现cache的相关操作;
BlockingCache:阻塞版本的缓存装饰器,保证只有一个线程到数据库去查找指定的key对应的数据;
缓存装饰器解读
FifoCache:先进先出缓存淘汰策略的缓存;
LoggingCache:日志能力的缓存;
ScheduledCache:定时清空的缓存;
BlockingCache:阻塞式缓存;
SerializedCache:序列化能力的缓存;
SynchronizedCache:进行同步控制的缓存;
思考:Mybatis的缓存功能使用HashMap实现会不会出现并发安全的问题?
锁粒度的问题 粗粒度锁

锁粒度的问题 细粒度锁(按key)

CacheKey解读
MyBatis中涉及到动态SQL的原因,缓存项的key不能仅仅通过一个String来表示,所以通过CacheKey来封装缓存的Key值,CacheKey可以封装多个影响缓存项的因素;判断两个CacheKey是否相同关键是比较两个对象的hash值是否一致;

orm框架查询数据过程

反射的核心类
反射是 Mybatis 模块中类最多的模块,通过反射实现了 POJO 对象的实例化和 POJO 的属性赋值,相对 JDK 自带的反射功能,MyBatis 的反射模块功能更为强大,性能更高;反射模块关键的几个类如下:
ObjectFactory:MyBatis 每次创建结果对象的新实例时,它都会使用对象工厂(ObjectFactory)去构建 POJO;
ReflectorFactory:创建 Reflector 的工厂类,Reflector 是MyBatis反射模块的基础,每个 Reflector对象都对应一个类,在其中缓存了反射操作所需要的类元信息;
ObjectWrapper:对对象的包装,抽象了对象的属性信息,他定义了一系列查询对象属性信息的方法,以及更新属性的方法;
ObjectWrapperFactory: ObjectWrapper 的工厂类,用于创建 ObjectWrapper ;
MetaObject:封装了对象元信息,包装了 MyBatis 中五个核心的反射类。也是提供给外部使用的反射工具类,可以利用它可以读取或者修改对象的属性信息;MetaObject 的类结构如下所示:

MyBatis源码骨架分析的更多相关文章
- MyBatis源码流程分析
mybatis核心流程三大阶段 Mybatis的初始化 建造者模式 建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象.这种类型的设计模式属于创建型模式,它提 ...
- mybatis源码简单分析
mybatis入门介绍 /** * 1. 接口式编程 * 原生: Dao =====> DaoImpl * mybatis : Mapper =====> xxxMapper * 2. S ...
- spring事务源码分析结合mybatis源码(三)
下面将结合mybatis源码来分析下,这种持久化框架是如何对connection使用,来达到spring事务的控制. 想要在把mybatis跟spring整合都需要这样一个jar包:mybatis-s ...
- Mybatis源码学习第六天(核心流程分析)之Executor分析
今Executor这个类,Mybatis虽然表面是SqlSession做的增删改查,其实底层统一调用的是Executor这个接口 在这里贴一下Mybatis查询体系结构图 Executor组件分析 E ...
- 精尽MyBatis源码分析 - MyBatis 的 SQL 执行过程(一)之 Executor
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- 精尽MyBatis源码分析 - SQL执行过程(二)之 StatementHandler
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- MyBatis源码分析-MyBatis初始化流程
MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...
- MyBatis源码分析-SQL语句执行的完整流程
MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...
- MyBatis源码分析(5)——内置DataSource实现
@(MyBatis)[DataSource] MyBatis源码分析(5)--内置DataSource实现 MyBatis内置了两个DataSource的实现:UnpooledDataSource,该 ...
随机推荐
- leetcode刷题笔记-1. 两数之和(java实现)
题目描述 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,数组中同一个元素不能使 ...
- ZoneJS 的原理与应用
目录 序言 Zone 是什么 ZoneJS 的原理 ZoneJS 的应用场景 参考 1. 序言 ZoneJS 是 Angular 团队受到 Dart 的 Zone 的启发,为 Angular v2 及 ...
- 栈及其简单应用(二)(python代码)
一.括号判定 前一篇文章我们介绍了栈的简单应用中,关于括号的判定,但那只是一种括号的判定,下面我们来介绍多种括号混合使用时,如何判断括号左右一一对应. 比如“{}{(}(][”这种情况,需要对一种括号 ...
- 每日一道 LeetCode (15):二进制求和
每天 3 分钟,走上算法的逆袭之路. 前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee ...
- Web前端性能优化,应该怎么做?
摘要:本文将分享一些前端性能优化的常用手段,包括减少请求次数.减小资源大小.各种缓存.预处理和长连接机制,以及代码方面的性能优化等方面. base64:尤其是在移动端,小图标可以base64(webp ...
- Unity3D制作类似吃鸡的小地图
先看效果图: 实现的效果就是右上角的一个小地图,会随着人物的移动而移动,显示人物的方向,并且可以展示地图设定范围的其他的玩家 制作起来也很简单,不需要任何代码.主要原理就是先创建Render Text ...
- STM32中 BOOT0 BOOT1设置(问题:程序下载进去但无法运行)
默认BOOT0接10K接地,BOOT1接10K接地 实际如果BOOT0不接10K到地,会导致程序能下载进去,但是无法运行情况
- 深度学习 | sklearn的train_test_split()各函数参数含义解释(超级全)
在机器学习中,我们通常将原始数据按照比例分割为"测试集"和"训练集",从 sklearn.model_selection 中调用train_test_split ...
- Linux上通过curl发送PUT和POST请求
通常而言,我们都使用curl发送get请求,但是还是可以使用它发送一些其他类型的请求的,如PUT/POST 只需要使用-X参数即可:
- JAVA8—————StringJoiner类
JAVA8——StringJoiner类引言:在阅读项目代码是,突然看到了StringJoiner这个类的使用,感觉很有意思,对实际开发中也有用,实际上是运用了StringBuilder的一个拼接字符 ...