源码包分析
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 的类结构如下所示: