浅入Mybatis源码
不对源码有兴趣的同学,真的很难坚持下去,加油奥里给
由官方文档入手:Mybatis3
结合《MyBatis技术内幕》
源码阅读网
Mybatis整体架构图
newInstance()和new()区别
- 两者创建对象的方式不同,前者是实用类的加载机制,后者则是直接创建一个类:
- newInstance创建类是这个类必须已经加载过且已经连接(Class.forName(“A”)这个过程),new创建类是则不需要这个类加载过
- newInstance 是弱类型(GC是回收对象的限制条件很低,容易被回收)、低效率、只能调用无参构造,new是强类型(GC不会自动回收,只有所有的指向对象的引用被移除是才会被回收,若对象生命周期已经结束,但引用没有被移除,经常会出现内存溢出)
- newInstance实例化对象只能调用无参构造方法(如果重写了一个带参构造方法,想要使用newInstance,则必须指定一个无参构造方法,否则会报初始化错误)
架构设计
导入依赖
- Mybatis 3.5.10
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
- 模块
大致明白每个模块的功能域
等等…
各包详细功能解析(主要)
org.apache.ibatis.binding:生成mapper 接口的动态代理并进行管理
org.apache.ibatis.builder:
- 包含Configuration对象所有构建器,主要包括XML、注解2种方式配置解析
- BaseBuilder 构建器基类
- XMLConfigBuilder 解析configuration.xml配置文件
- XMLMapperBuilder 解析Mapper.xml配置文件
- XMLStatementBuilder 解析select\update\delete 标签
- MapperAnnotationBuilder 注解式Mapper
org.apache.ibatis.cache:
- 缓存功能实现、包含各种缓存装饰器
- TransactionalCache 二级缓存功能实现
org.apache.ibatis.cursor:实现游标的方式查询数据、游标非常适合处理百万级别的数据查询
org.apache.ibatis.datasource:数据源 包括jndi数据源、连接池功能
org.apache.ibatis.executor:
- 包含SQL语句执行器,核心功能包
- 功能包括:主键生成功能、执行参数解析功能、执行结果集解析功能、SQL执行器、缓存执行器
org.apache.ibatis.jdbc:
- JDBC一些操作
- SqlRunner SQL执行
- ScriptRunner 脚本执行,可以执行建库语句
org.apache.ibatis.logging:
- 日志功能,实现多种日志框架的对接
- org.apache.ibatis.logging.jdbc 代理所有功能JDBC 操作,实现了在debug模式下能够输出SQL
org.apache.ibatis.mapping:配置文件与实体对象的映射功能,Mapper映射、参数映射、结果映射等
org.apache.ibatis.parsing:
- 解析工具包
- GenericTokenParser:解析#{} ${} 这种占位符
- XPathParser:XPath形式解析XML
- PropertyParser: properties解析器
org.apache.ibatis.session:
- 主要实现SqlSession功能,非常核心包
- 官方注释:SqlSession包含了MyBatis工作的所有的Java接口,通过这些接口你可以
执行SQL命令(insert\delete\update\select),获取Mapper,管理实务
org.apache.ibatis.transaction:事务功能实现,包装了数据库连接,处理数据库连接生命周期包括:连接创建,预编译,提交\回滚和关闭
org.apache.ibatis.type:类型处理器,包括所有数据库类型对应Java类型的处理器,如果要实现自己类型处理器就需要实现包下的基础接口
思路:
同学们一拿到源码肯定不知道如何入手,大家都说Mybatis的源码是最简单,最少的,仅仅只在缓存那块有点多,最为一名刚入手源码的小白,这些对我来说太难了吧
因此:最佳阅读源码的方式就是能跑起来Debug,以官方提供的文档,寻找入口,一步一步深入
准备
模块
- MybatisNoXmlDemo(启动类)
public class MybatisNoXmlDemo {
public static void main(String[] args) {
DataSource dataSource = getDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
SqlSession sqlSession = sqlSessionFactory.openSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
System.out.println(blogMapper.selectBlog(1));
}
private static DataSource getDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl("jdbc:mysql://localhost:3306/haierbatis?characterEncoding=utf8");
druidDataSource.setUsername("root");
druidDataSource.setPassword("root");
return druidDataSource;
}
}
- Blog
@Data
public class Blog {
private Integer id;
private String blog_name;
}
- BlogMapper
public interface BlogMapper {
Blog selectBlog(Integer id);
}
- BlogMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.haier.mybatis.Mapper.BlogMapper">
<select id="selectBlog" resultType="com.haier.mybatis.Pojo.Blog">
select * from blog where id = #{id}
</select>
</mapper>
Debug大致步骤
初始化
执行器
Excutor
- org.apache.ibatis.executor
在这些Executor接口实现中涉及两种设计模式:模板和装饰器模式
- 模板模式:模板方法模式可以将模板方法以及固定不变的方法统一封装到父类中,而将变化的部分封装到子类中实现。
- 装饰器:允许向一个现有的对象添加新的功能,同时又不改变其结构。
CachingExecutor 扮演了装饰器的角色,为Executor添加了二级缓存的功能,
BaseExecutor
- org.apache.ibatis.executo
- BaseExecutor是一个实现了Executor接口的抽象类,它实现了Executor接口的大部分方法,其中就使用了模板方法模式。
- BaseExecutor中主要提供了缓存管理和事务管理的基本功能。
- 继承BaseExecutor的子类只要实现四个基本方法来完成数据库的相关操作即可,这四个方法分别是: doUpdate()方法、doOueryO方法、doOuervCursor方法、doFlushStatement0方法,其余的功能在 BaseExecutor 中实现。
字段
方法
BaseExecutor.doQuery()流程图
何为CacheKey对象,以及如何保证缓存的唯一Key?
CacheKey对象
//TODO 18点43分
Mybatis缓存
MyBatis中目带的这两级缓存与MyBatis 以及整个应用是运行在同一个JVM中的,共享同一块堆内存。如果这两级缓存中的数据量较大,则可能影响系统中其他功能的运行,所以当需要缓存大量数据时,优先考虑使用Redis、Memcache等缓存产品。
在 MyBatis 地开启一个 SqlSession 会话时,都会创建一个 Executor 执行器对象
一级缓存:它是sqlSession对象的缓存,自带的(不需要配置)不可关闭的(不想使用还不行). 一级缓存的生命周期与sqlSession一致。
二级缓存:它是SqlSessionFactory的缓存。只要是同一个SqlSessionFactory创建的SqlSession就共享二级缓存的内容,并且可以操作二级缓存。二级缓存如果要使用的话,需要我们自己手动开启(需要配置的)。
Cache:MyBatis的缓存接口
PerpetualCache:缓存的实际存储类,使用一个HashMap作为缓存容器,实现了简单的缓存功能(非线程安全),get和put等操作都是直接调用HaspMap的相应方法实现。decorators包下其它Cache实现类都使用了装饰器模式,在该类的基础上添加了其他功能。
BlockingCache/SynchronizedCache:通过在get/put方式中加锁,保证只有一个线程操作缓存
FifoCache、LruCache:当缓存到达上限时候,通过FIFO或者LRU(最早进入内存)策略删除缓存
ScheduledCache:在进行get/put/remove/getSize等操作前,判断缓存时间是否超过了设置的最长缓存时间
SoftCache/WeakCache:通过JVM的软引用和弱引用来实现缓存,当JVM内存不足时,会自动清理掉这些缓存
TransactionalCacheManager:用于管理CachingExecutor使用的二级缓存对象
一级缓存
-
一级缓存在 Executor 执行器(SimpleExecutor)中有一个 Cache 对象中,默认就是一个 HashMap 存储缓存数据,执行数据库查询操作前,如果在一级缓存中有对应的缓存数据,则直接返回。
-
一级缓存存在于SimpleExecutor,因为如果没有指定特殊的执行器,BaseExecutor默认采用简单执行器SimpleExecutor.
命中场景
流程
- 一级缓存从数据库查找数据的接口实doquery()
- 缓存数据存在PerpetutalCache