MyBatis 中的缓存分为一级缓存、二级缓存,但在本质上是相同的,它们使用的都是 Cache 接口 的实现。MyBatis 缓存模块 的设计,使用了装饰器模式,这里不对此进行过多解析,以后会专门开一篇博文分析常用框架中使用到的设计模式。

1 Cache 组件

MyBatis 中缓存模块相关的代码位于 org.apache.ibatis.cache 包 下,其中 Cache 接口 是缓存模块中最核心的接口,它定义了所有缓存的基本行为。

  1. public interface Cache {
  2. /**
  3. * 获取当前缓存的 Id
  4. */
  5. String getId();
  6. /**
  7. * 存入缓存的 key 和 value,key 一般为 CacheKey对象
  8. */
  9. void putObject(Object key, Object value);
  10. /**
  11. * 根据 key 获取缓存值
  12. */
  13. Object getObject(Object key);
  14. /**
  15. * 删除指定的缓存项
  16. */
  17. Object removeObject(Object key);
  18. /**
  19. * 清空缓存
  20. */
  21. void clear();
  22. /**
  23. * 获取缓存的大小
  24. */
  25. int getSize();
  26. /**
  27. * !!!!!!!!!!!!!!!!!!!!!!!!!!
  28. * 获取读写锁,可以看到,这个接口方法提供了默认的实现!!
  29. * 这是 Java8 的新特性!!只是平时开发时很少用到!!!
  30. * !!!!!!!!!!!!!!!!!!!!!!!!!!
  31. */
  32. default ReadWriteLock getReadWriteLock() {
  33. return null;
  34. }
  35. }

如下图所示,Cache 接口 的实现类有很多,但大部分都是装饰器,只有 PerpetualCache 提供了 Cache 接口 的基本实现。

avatar

1.1 PerpetualCache

PerpetualCache(Perpetual:永恒的,持续的)在缓存模块中扮演着被装饰的角色,其实现比较简单,底层使用 HashMap 记录缓存项,也是通过该 HashMap 对象 的方法实现的 Cache 接口 中定义的相应方法。

  1. public class PerpetualCache implements Cache {
  2. // Cache对象 的唯一标识
  3. private final String id;
  4. // 其所有的缓存功能实现,都是基于 JDK 的 HashMap 提供的方法
  5. private Map<Object, Object> cache = new HashMap<>();
  6. public PerpetualCache(String id) {
  7. this.id = id;
  8. }
  9. @Override
  10. public String getId() {
  11. return id;
  12. }
  13. @Override
  14. public int getSize() {
  15. return cache.size();
  16. }
  17. @Override
  18. public void putObject(Object key, Object value) {
  19. cache.put(key, value);
  20. }
  21. @Override
  22. public Object getObject(Object key) {
  23. return cache.get(key);
  24. }
  25. @Override
  26. public Object removeObject(Object key) {
  27. return cache.remove(key);
  28. }
  29. @Override
  30. public void clear() {
  31. cache.clear();
  32. }
  33. /**
  34. * 其重写了 Object 中的 equals() 和 hashCode()方法,两者都只关心 id字段
  35. */
  36. @Override
  37. public boolean equals(Object o) {
  38. if (getId() == null) {
  39. throw new CacheException("Cache instances require an ID.");
  40. }
  41. if (this == o) {
  42. return true;
  43. }
  44. if (!(o instanceof Cache)) {
  45. return false;
  46. }
  47. Cache otherCache = (Cache) o;
  48. return getId().equals(otherCache.getId());
  49. }
  50. @Override
  51. public int hashCode() {
  52. if (getId() == null) {
  53. throw new CacheException("Cache instances require an ID.");
  54. }
  55. return getId().hashCode();
  56. }
  57. }

下面来看一下 cache.decorators 包 下提供的装饰器,它们都直接实现了 Cache 接口,扮演着装饰器的角色。这些装饰器会在 PerpetualCache 的基础上提供一些额外的功能,通过多个组合后满足一个特定的需求。

1.2 BlockingCache

BlockingCache 是阻塞版本的缓存装饰器,它会保证只有一个线程到数据库中查找指定 key 对应的数据。

  1. public class BlockingCache implements Cache {
  2. // 阻塞超时时长
  3. private long timeout;
  4. // 持有的被装饰者
  5. private final Cache delegate;
  6. // 每个 key 都有其对应的 ReentrantLock锁对象
  7. private final ConcurrentHashMap<Object, ReentrantLock> locks;
  8. // 初始化 持有的持有的被装饰者 和 锁集合
  9. public BlockingCache(Cache delegate) {
  10. this.delegate = delegate;
  11. this.locks = new ConcurrentHashMap<>();
  12. }
  13. }

假设 线程 A 在 BlockingCache 中未查找到 keyA 对应的缓存项时,线程 A 会获取 keyA 对应的锁,这样,线程 A 在后续查找 keyA 时,其它线程会被阻塞。

  1. // 根据 key 获取锁对象,然后上锁
  2. private void acquireLock(Object key) {
  3. // 获取 key 对应的锁对象
  4. Lock lock = getLockForKey(key);
  5. // 获取锁,带超时时长
  6. if (timeout > 0) {
  7. try {
  8. boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);
  9. if (!acquired) { // 超时,则抛出异常
  10. throw new CacheException("Couldn't get a lock in " + timeout + " for the key " + key + " at the cache " + delegate.getId());
  11. }
  12. } catch (InterruptedException e) {
  13. // 如果获取锁失败,则阻塞一段时间
  14. throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);
  15. }
  16. } else {
  17. // 上锁
  18. lock.lock();
  19. }
  20. }
  21. private ReentrantLock getLockForKey(Object key) {
  22. // Java8 新特性,Map系列类 中新增的方法
  23. // V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
  24. // 表示,若 key 对应的 value 为空,则将第二个参数的返回值存入该 Map集合 并返回
  25. return locks.computeIfAbsent(key, k -> new ReentrantLock());
  26. }

假设 线程 A 从数据库中查找到 keyA 对应的结果对象后,将结果对象放入到 BlockingCache 中,此时 线程 A 会释放 keyA 对应的锁,唤醒阻塞在该锁上的线程。其它线程即可从 BlockingCache 中获取 keyA 对应的数据,而不是再次访问数据库。

  1. @Override
  2. public void putObject(Object key, Object value) {
  3. try {
  4. // 存入 key 和其对应的缓存项
  5. delegate.putObject(key, value);
  6. } finally {
  7. // 最后释放锁
  8. releaseLock(key);
  9. }
  10. }
  11. private void releaseLock(Object key) {
  12. ReentrantLock lock = locks.get(key);
  13. // 锁是否被当前线程持有
  14. if (lock.isHeldByCurrentThread()) {
  15. // 是,则释放锁
  16. lock.unlock();
  17. }
  18. }

1.3 FifoCache 和 LruCache

在很多场景中,为了控制缓存的大小,系统需要按照一定的规则清理缓存。FifoCache 是先入先出版本的装饰器,当向缓存添加数据时,如果缓存项的个数已经达到上限,则会将缓存中最老(即最早进入缓存)的缓存项删除。

  1. public class FifoCache implements Cache {
  2. // 被装饰对象
  3. private final Cache delegate;
  4. // 用一个 FIFO 的队列记录 key 的顺序,其具体实现为 LinkedList
  5. private final Deque<Object> keyList;
  6. // 决定了缓存的容量上限
  7. private int size;
  8. // 国际惯例,通过构造方法初始化自己的属性,缓存容量上限默认为 1024个
  9. public FifoCache(Cache delegate) {
  10. this.delegate = delegate;
  11. this.keyList = new LinkedList<>();
  12. this.size = 1024;
  13. }
  14. @Override
  15. public String getId() {
  16. return delegate.getId();
  17. }
  18. @Override
  19. public int getSize() {
  20. return delegate.getSize();
  21. }
  22. public void setSize(int size) {
  23. this.size = size;
  24. }
  25. @Override
  26. public void putObject(Object key, Object value) {
  27. // 存储缓存项之前,先在 keyList 中注册
  28. cycleKeyList(key);
  29. // 存储缓存项
  30. delegate.putObject(key, value);
  31. }
  32. private void cycleKeyList(Object key) {
  33. // 在 keyList队列 中注册要添加的 key
  34. keyList.addLast(key);
  35. // 如果注册这个 key 会超出容积上限,则把最老的一个缓存项清除掉
  36. if (keyList.size() > size) {
  37. Object oldestKey = keyList.removeFirst();
  38. delegate.removeObject(oldestKey);
  39. }
  40. }
  41. @Override
  42. public Object getObject(Object key) {
  43. return delegate.getObject(key);
  44. }
  45. @Override
  46. public Object removeObject(Object key) {
  47. return delegate.removeObject(key);
  48. }
  49. // 除了清理缓存项,还要清理 key 的注册列表
  50. @Override
  51. public void clear() {
  52. delegate.clear();
  53. keyList.clear();
  54. }
  55. }

LruCache 是按照”近期最少使用算法”(Least Recently Used, LRU)进行缓存清理的装饰器,在需要清理缓存时,它会清除最近最少使用的缓存项。

  1. public class LruCache implements Cache {
  2. // 被装饰者
  3. private final Cache delegate;
  4. // 这里使用的是 LinkedHashMap,它继承了 HashMap,但它的元素是有序的
  5. private Map<Object, Object> keyMap;
  6. // 最近最少被使用的缓存项的 key
  7. private Object eldestKey;
  8. // 国际惯例,构造方法中进行属性初始化
  9. public LruCache(Cache delegate) {
  10. this.delegate = delegate;
  11. // 这里初始化了 keyMap,并定义了 eldestKey 的取值规则
  12. setSize(1024);
  13. }
  14. public void setSize(final int size) {
  15. // 初始化 keyMap,同时指定该 Map 的初始容积及加载因子,第三个参数true 表示 该LinkedHashMap
  16. // 记录的顺序是 accessOrder,即,LinkedHashMap.get()方法 会改变其中元素的顺序
  17. keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
  18. private static final long serialVersionUID = 4267176411845948333L;
  19. // 当调用 LinkedHashMap.put()方法 时,该方法会被调用
  20. @Override
  21. protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
  22. boolean tooBig = size() > size;
  23. if (tooBig) {
  24. // 当已达到缓存上限,更新 eldestKey字段,后面将其删除
  25. eldestKey = eldest.getKey();
  26. }
  27. return tooBig;
  28. }
  29. };
  30. }
  31. // 存储缓存项
  32. @Override
  33. public void putObject(Object key, Object value) {
  34. delegate.putObject(key, value);
  35. // 记录缓存项的 key,超出容量则清除最久未使用的缓存项
  36. cycleKeyList(key);
  37. }
  38. private void cycleKeyList(Object key) {
  39. keyMap.put(key, key);
  40. // eldestKey 不为空,则表示已经达到缓存上限
  41. if (eldestKey != null) {
  42. // 清除最久未使用的缓存
  43. delegate.removeObject(eldestKey);
  44. // 制空
  45. eldestKey = null;
  46. }
  47. }
  48. @Override
  49. public Object getObject(Object key) {
  50. // 访问 key元素 会改变该元素在 LinkedHashMap 中的顺序
  51. keyMap.get(key); //touch
  52. return delegate.getObject(key);
  53. }
  54. @Override
  55. public String getId() {
  56. return delegate.getId();
  57. }
  58. @Override
  59. public int getSize() {
  60. return delegate.getSize();
  61. }
  62. @Override
  63. public Object removeObject(Object key) {
  64. return delegate.removeObject(key);
  65. }
  66. @Override
  67. public void clear() {
  68. delegate.clear();
  69. keyMap.clear();
  70. }
  71. }

1.4 SoftCache 和 WeakCache

在分析 SoftCache 和 WeakCache 实现之前,我们再温习一下 Java 提供的 4 种引用类型,强引用 StrongReference、软引用 SoftReference、弱引用 WeakReference 和虚引用 PhantomReference。

  • 强引用 平时用的最多的,如 Object obj = new Object(),新建的 Object 对象 就是被强引用的。如果一个对象被强引用,即使是 JVM 内存空间不足,要抛出 OutOfMemoryError 异常,GC 也绝不会回收该对象。
  • 软引用 仅次于强引用的一种引用,它使用类 SoftReference 来表示。当 JVM 内存不足时,GC 会回收那些只被软引用指向的对象,从而避免内存溢出。软引用适合引用那些可以通过其他方式恢复的对象,例如, 数据库缓存中的对象就可以从数据库中恢复,所以软引用可以用来实现缓存,下面要介绍的 SoftCache 就是通过软引用实现的。
    另外,由于在程序使用软引用之前的某个时刻,其所指向的对象可能己经被 GC 回收掉了,所以通过 Reference.get()方法 来获取软引用所指向的对象时,总是要通过检查该方法返回值是否为 null,来判断被软引用的对象是否还存活。
  • 弱引用 弱引用使用 WeakReference 表示,它不会阻止所引用的对象被 GC 回收。在 JVM 进行垃圾回收时,如果指向一个对象的所有引用都是弱引用,那么该对象会被回收。 所以,只被弱引用所指向的对象,其生存周期是 两次 GC 之间 的这段时间,而只被软引用所指向的对象可以经历多次 GC,直到出现内存紧张的情况才被回收。
  • 虚引用 最弱的一种引用类型,由类 PhantomReference 表示。虚引用可以用来实现比较精细的内存使用控制,但很少使用。
  • 引用队列(ReferenceQueue ) 很多场景下,我们的程序需要在一个对象被 GC 时得到通知,引用队列就是用于收集这些信息的队列。在创建 SoftReference 对象 时,可以为其关联一个引用队列,当 SoftReference 所引用的对象被 GC 时, JVM 就会将该 SoftReference 对象 添加到与之关联的引用队列中。当需要检测这些通知信息时,就可以从引用队列中获取这些 SoftReference 对象。不仅是 SoftReference,弱引用和虚引用都可以关联相应的队列。

现在来看一下 SoftCache 的具体实现。

  1. public class SoftCache implements Cache {
  2. // 这里使用了 LinkedList 作为容器,在 SoftCache 中,最近使用的一部分缓存项不会被 GC
  3. // 这是通过将其 value 添加到 hardLinksToAvoidGarbageCollection集合 实现的(即,有强引用指向其value)
  4. private final Deque<Object> hardLinksToAvoidGarbageCollection;
  5. // 引用队列,用于记录已经被 GC 的缓存项所对应的 SoftEntry对象
  6. private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
  7. // 持有的被装饰者
  8. private final Cache delegate;
  9. // 强连接的个数,默认为 256
  10. private int numberOfHardLinks;
  11. // 构造方法进行属性的初始化
  12. public SoftCache(Cache delegate) {
  13. this.delegate = delegate;
  14. this.numberOfHardLinks = 256;
  15. this.hardLinksToAvoidGarbageCollection = new LinkedList<>();
  16. this.queueOfGarbageCollectedEntries = new ReferenceQueue<>();
  17. }
  18. private static class SoftEntry extends SoftReference<Object> {
  19. private final Object key;
  20. SoftEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
  21. // 指向 value 的引用是软引用,并且关联了 引用队列
  22. super(value, garbageCollectionQueue);
  23. // 强引用
  24. this.key = key;
  25. }
  26. }
  27. @Override
  28. public void putObject(Object key, Object value) {
  29. // 清除已经被 GC 的缓存项
  30. removeGarbageCollectedItems();
  31. // 添加缓存
  32. delegate.putObject(key, new SoftEntry(key, value, queueOfGarbageCollectedEntries));
  33. }
  34. private void removeGarbageCollectedItems() {
  35. SoftEntry sv;
  36. // 遍历 queueOfGarbageCollectedEntries集合,清除已经被 GC 的缓存项 value
  37. while ((sv = (SoftEntry) queueOfGarbageCollectedEntries.poll()) != null) {
  38. delegate.removeObject(sv.key);
  39. }
  40. }
  41. @Override
  42. public Object getObject(Object key) {
  43. Object result = null;
  44. @SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cache
  45. // 用一个软引用指向 key 对应的缓存项
  46. SoftReference<Object> softReference = (SoftReference<Object>) delegate.getObject(key);
  47. // 检测缓存中是否有对应的缓存项
  48. if (softReference != null) {
  49. // 获取 softReference 引用的 value
  50. result = softReference.get();
  51. // 如果 softReference 引用的对象已经被 GC,则从缓存中清除对应的缓存项
  52. if (result == null) {
  53. delegate.removeObject(key);
  54. } else {
  55. synchronized (hardLinksToAvoidGarbageCollection) {
  56. // 将缓存项的 value 添加到 hardLinksToAvoidGarbageCollection集合 中保存
  57. hardLinksToAvoidGarbageCollection.addFirst(result);
  58. // 如果 hardLinksToAvoidGarbageCollection 的容积已经超过 numberOfHardLinks
  59. // 则将最老的缓存项从 hardLinksToAvoidGarbageCollection 中清除,FIFO
  60. if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {
  61. hardLinksToAvoidGarbageCollection.removeLast();
  62. }
  63. }
  64. }
  65. }
  66. return result;
  67. }
  68. @Override
  69. public Object removeObject(Object key) {
  70. // 清除指定的缓存项之前,也会先清理被 GC 的缓存项
  71. removeGarbageCollectedItems();
  72. return delegate.removeObject(key);
  73. }
  74. @Override
  75. public void clear() {
  76. synchronized (hardLinksToAvoidGarbageCollection) {
  77. // 清理强引用集合
  78. hardLinksToAvoidGarbageCollection.clear();
  79. }
  80. // 清理被 GC 的缓存项
  81. removeGarbageCollectedItems();
  82. // 清理最底层的缓存项
  83. delegate.clear();
  84. }
  85. @Override
  86. public String getId() {
  87. return delegate.getId();
  88. }
  89. @Override
  90. public int getSize() {
  91. removeGarbageCollectedItems();
  92. return delegate.getSize();
  93. }
  94. public void setSize(int size) {
  95. this.numberOfHardLinks = size;
  96. }
  97. }

WeakCache 的实现与 SoftCache 基本类似,唯一的区别在于其中使用 WeakEntry(继承了 WeakReference)封装真正的 value 对象,其他实现完全一样。

另外,还有 ScheduledCache、LoggingCache、SynchronizedCache、SerializedCache 等。ScheduledCache 是周期性清理缓存的装饰器,它的 clearInterval 字段 记录了两次缓存清理之间的时间间隔,默认是一小时,lastClear 字段 记录了最近一次清理的时间戳。 ScheduledCache 的 getObject()、putObject()、removeObject() 等核心方法,在执行时都会根据这两个字段检测是否需要进行清理操作,清理操作会清空缓存中所有缓存项。

LoggingCache 在 Cache 的基础上提供了日志功能,它通过 hit 字段 和 request 字段 记录了 Cache 的命中次数和访问次数。在 LoggingCache.getObject()方法 中,会统计命中次数和访问次数 这两个指标,井按照指定的日志输出方式输出命中率。

SynchronizedCache 通过在每个方法上添加 synchronized 关键字,为 Cache 添加了同步功能,有点类似于 JDK 中 Collections 的 SynchronizedCollection 内部类。

SerializedCache 提供了将 value 对象 序列化的功能。SerializedCache 在添加缓存项时,会将 value 对应的 Java 对象 进行序列化,井将序列化后的 byte[]数组 作为 value 存入缓存 。 SerializedCache 在获取缓存项时,会将缓存项中的 byte[]数组 反序列化成 Java 对象。不使用 SerializedCache 装饰器 进行装饰的话,每次从缓存中获取同一 key 对应的对象时,得到的都是同一对象,任意一个线程修改该对象都会影响到其他线程,以及缓存中的对象。而使用 SerializedCache 每次从缓存中获取数据时,都会通过反序列化得到一个全新的对象。 SerializedCache 使用的序列化方式是 Java 原生序列化。

2 CacheKey

在 Cache 中唯一确定一个缓存项,需要使用缓存项的 key 进行比较,MyBatis 中因为涉及 动态 SQL 等多方面因素, 其缓存项的 key 不能仅仅通过一个 String 表示,所以 MyBatis 提供了 CacheKey 类 来表示缓存项的 key,在一个 CacheKey 对象 中可以封装多个影响缓存项的因素。 CacheKey 中可以添加多个对象,由这些对象共同确定两个 CacheKey 对象 是否相同。

  1. public class CacheKey implements Cloneable, Serializable {
  2. private static final long serialVersionUID = 1146682552656046210L;
  3. public static final CacheKey NULL_CACHE_KEY = new NullCacheKey();
  4. private static final int DEFAULT_MULTIPLYER = 37;
  5. private static final int DEFAULT_HASHCODE = 17;
  6. // 参与计算hashcode,默认值DEFAULT_MULTIPLYER = 37
  7. private final int multiplier;
  8. // 当前CacheKey对象的hashcode,默认值DEFAULT_HASHCODE = 17
  9. private int hashcode;
  10. // 校验和
  11. private long checksum;
  12. private int count;
  13. // 由该集合中的所有元素 共同决定两个CacheKey对象是否相同,一般会使用一下四个元素
  14. // MappedStatement的id、查询结果集的范围参数(RowBounds的offset和limit)
  15. // SQL语句(其中可能包含占位符"?")、SQL语句中占位符的实际参数
  16. private List<Object> updateList;
  17. // 构造方法初始化属性
  18. public CacheKey() {
  19. this.hashcode = DEFAULT_HASHCODE;
  20. this.multiplier = DEFAULT_MULTIPLYER;
  21. this.count = 0;
  22. this.updateList = new ArrayList<>();
  23. }
  24. public CacheKey(Object[] objects) {
  25. this();
  26. updateAll(objects);
  27. }
  28. public void update(Object object) {
  29. int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
  30. // 重新计算count、checksum和hashcode的值
  31. count++;
  32. checksum += baseHashCode;
  33. baseHashCode *= count;
  34. hashcode = multiplier * hashcode + baseHashCode;
  35. // 将object添加到updateList集合
  36. updateList.add(object);
  37. }
  38. public int getUpdateCount() {
  39. return updateList.size();
  40. }
  41. public void updateAll(Object[] objects) {
  42. for (Object o : objects) {
  43. update(o);
  44. }
  45. }
  46. /**
  47. * CacheKey重写了 equals() 和 hashCode()方法,这两个方法使用上面介绍
  48. * 的 count、checksum、hashcode、updateList 比较两个 CacheKey对象 是否相同
  49. */
  50. @Override
  51. public boolean equals(Object object) {
  52. // 如果为同一对象,直接返回 true
  53. if (this == object) {
  54. return true;
  55. }
  56. // 如果 object 都不是 CacheKey类型,直接返回 false
  57. if (!(object instanceof CacheKey)) {
  58. return false;
  59. }
  60. // 类型转换一下
  61. final CacheKey cacheKey = (CacheKey) object;
  62. // 依次比较 hashcode、checksum、count,如果不等,直接返回 false
  63. if (hashcode != cacheKey.hashcode) {
  64. return false;
  65. }
  66. if (checksum != cacheKey.checksum) {
  67. return false;
  68. }
  69. if (count != cacheKey.count) {
  70. return false;
  71. }
  72. // 比较 updateList 中的元素是否相同,不同直接返回 false
  73. for (int i = 0; i < updateList.size(); i++) {
  74. Object thisObject = updateList.get(i);
  75. Object thatObject = cacheKey.updateList.get(i);
  76. if (!ArrayUtil.equals(thisObject, thatObject)) {
  77. return false;
  78. }
  79. }
  80. return true;
  81. }
  82. @Override
  83. public int hashCode() {
  84. return hashcode;
  85. }
  86. @Override
  87. public String toString() {
  88. StringJoiner returnValue = new StringJoiner(":");
  89. returnValue.add(String.valueOf(hashcode));
  90. returnValue.add(String.valueOf(checksum));
  91. updateList.stream().map(ArrayUtil::toString).forEach(returnValue::add);
  92. return returnValue.toString();
  93. }
  94. @Override
  95. public CacheKey clone() throws CloneNotSupportedException {
  96. CacheKey clonedCacheKey = (CacheKey) super.clone();
  97. clonedCacheKey.updateList = new ArrayList<>(updateList);
  98. return clonedCacheKey;
  99. }
  100. }

3 小结

至此 Mybatis 的基础支持层的主要模块就分析完了。本模块首先介绍了 MyBatis 对 Java 反射机制的封装;然后分析了类型转换 TypeHandler 组件,了解了 MyBatis 如何实现数据在 Java 类型 与 JDBC 类型 之间的转换。

之后分析了 MyBatis 提供的 DataSource 模块 的实现和原理,深入解析了 MyBatis 自带的连接池 PooledDataSource 的详细实现;后面紧接着介绍了 Transaction 模块 的功能。然后分析了 binding 模块 如何将 Mapper 接口 与映射配置信息相关联,以及其中的原理。最后介绍了 MyBatis 的缓存模块,分析了 Cache 接口 以及多个实现类的具体实现,它们是 Mybatis 中一级缓存和二级缓存的基础。