目录:
1、开发重点知识
- MyBatis快速入门
- result 与 resultMap
- 怎么样传递多个参数
- 获取主键
- SQL 的参数
- 代码生成器
- 关联查询
- 缓存
- 与 spring 的集成
2、源码骨架分析
- 整体架构
- 日志模块分析
- 数据源模块分析
- 缓存模块分析
- 反射模块分析
3、源码流程分析
- 流程整体分析
- 配置加载阶段
- 代理阶段分析
- 数据读写阶段
4、知识拾遗
- 与 spring 的结合源码分析
- 插件开发
- 手写 MyBatis
MyBatis - 2,源码骨架分析
1、整体架构
分层的好处:
- 架构功能模块
- 提高开发效率
- 提高系统伸缩性和性能
外观模式(门面模式)
门面模式:提供了一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,让子系统更容易使用。
优点:
- 使复杂子系统的接口变的简单可用,减少了客户端对子系统的依赖,达到了解耦的效果;遵循了OO原则中的迪米特法则,对内封装具体细节,对外只暴露必要的接口。
使用场景:
- 一个复杂的模块或子系统提供一个供外界访问的接口
- 子系统相对独立 ― 外界对子系统的访问只要黑箱操作即可
谈谈设计模式的几个原则
- 单一职责原则:一个类或者一个接口只负责唯一项职责,尽量设计出功能单一的接口;
- 依赖倒转原则:高层模块不应该依赖低层模块具体实现,解耦高层与低层。既面向接口编程,当实现发生变化时,只需提供新的实现类,不需要修改高层模块代码;
- 开放-封闭原则:程序对外扩展开放,对修改关闭;换句话说,当需求发生变化时,我们可以通过添加新模块来满足新需求,而不是通过修改原来的实现代码来满足新需求;
- 迪米特法则:一个对象应该对其他对象保持最少的了解,尽量降低类与类之间的耦合度;(尽量降低类的访问权限,类之间不要有强)
- 里氏代换原则:所有引用基类(父类)的地方必须能透明地使用其子类的对象;
- 接口隔离原则:客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上;
2、日志模块分析
2.1
MyBatis没有提供日志的实现类,需要接入第三方的日志组件,但第三方日志组件都有各自的Log级别,且各不相同,而MyBatis统一提供了trace、debug、warn、error四个级别;
自动扫描日志实现,并且第三方日志插件加载优先级如下:slf4J → commonsLoging → Log4J2 → Log4J → JdkLog;
日志的使用要优雅的嵌入到主体功能中;
2.2 适配器模式
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁,将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作;
适用场景:当调用双方都不太容易修改的时候,为了复用现有组件可以使用适配器模式;在系统中接入第三方组件的时候经常被使用到;
注意:如果系统中存在过多的适配器,会增加系统的复杂性,设计人员应考虑对系统进行重构;
2.3 代理模式
定义:给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用;
目的:
(1)通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;
(2)通过代理对象对原有的业务增强;
2.4 代码梳理
集成slf4j、log4j、log4j2、jdkLog、
自定义一个Log 接口:(适配器模式)
创建一个实现Log接口的MyBatis自己的Slf4jImpl实现类,
创建一个实现Log接口的slf4j的Slf4jLoggerImpl的实现类
LogFactory 工厂模式
// 关键成员变量
private static Constructor<? extends Log> logConstructor;
使用静态代码块,设置集成的外部日志框架的加载顺序
static{
tryImplementaion(() -> {useSlf4jLogging();});
。。。
}
private static void tryImplementation(Runnable runable){
if(logConstructor == null) {
try {
runable.run();
}catch() {
// ignore
}
}
}
public static synchronized void useSlf4jLogging() {
setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class);
}
private static void setImplementation(Class<? extends Log> implClass) {
try {
Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
Log log = candidata.newInstance(LogFactory.class.getName());
if (log.isDebugEnable()) {
log.debug("Logging initialized using ");
}
logConstructor = candidate;
} catch (Throwable t) {
throw new LogException("");
}
}
private LogFactory() {
// disable construction
}
3、数据源模块分析
3.1 基础支撑层源码分析 创建一个数据源的难点
常见的数据源组件都实现了javax.sql.DataSource接口;
MyBatis不但要能集成第三方的数据源组件,自身也提供了数据源的实现;
一般情况下,数据源的初始化过程参数较多,比较复杂;
3.2 工厂模式uml类图
3.3 数据源模块类图
3.4 为什么要使用工厂模式
4、缓存模块分析
锁粒度的问题 粗粒度锁
锁粒度的问题 细粒度锁(按key)
CacheKey解读
MyBatis中涉及到动态SQL的原因,缓存项的key不能仅仅通过一个String来表示,所以通过CacheKey来封装缓存的Key值,CacheKey可以封装多个影响缓存项的因素;判断两个CacheKey是否相同关键是比较两个对象的hash值是否一致;
5、反射模块分析
orm框架查询数据过程
反射的核心类
MetaObject:封装了对象元信息,包装了MyBatis中五个核心的反射类。也是提供给外部使用的反射工具类,可以利用它可以读取或者修改对象的属性信息;