Mybatis缓存策略源码

Mybatis3.5.1源码分析

  1. Mybatis-SqlSessionFactoryBuilder,XMLConfigBuilder,XPathParser源码解析
  2. Mybatis-Configuration源码解析
  3. Mybatis-事务对象源码解析
  4. Mybatis-数据源源码解析
  5. Mybatis缓存策略源码解析
  6. Mybatis-DatabaseIdProvider源码解析
  7. Mybatis-TypeHandler源码解析
  8. Mybatis-Reflector源码解析
  9. Mybatis-ObjectFactory,ObjectWrapperFactory源码分析
  10. Mybatis-Mapper各类标签封装类源码解析
  11. Mybatis-XMLMapperBuilder,XMLStatmentBuilder源码分析
  12. Mybatis-MapperAnnotationBuilder源码分析
  13. [Mybatis-MetaObject,MetaClass源码解析]https://www.jianshu.com/p/f51fa552f30a)
  14. Mybatis-LanguageDriver源码解析
  15. Mybatis-SqlSource源码解析
  16. Mybatis-SqlNode源码解析
  17. Mybatis-KeyGenerator源码解析
  18. Mybatis-Executor源码解析
  19. Mybatis-ParameterHandler源码解析
  20. Mybatis-StatementHandler源码解析
  21. Mybatis-DefaultResultSetHandler(一)源码解析
  22. Mybatis-DefaultResultSetHandler(二)源码解析
  23. Mybatis-ResultHandler,Cursor,RowBounds 源码分析
  24. Mybatis-MapperProxy源码解析
  25. Mybatis-SqlSession源码解析
  26. Mybatis-Interceptor源码解析

Cache

缓存策略类都是基于装饰者设计模式进行开发的

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache;

import java.util.concurrent.locks.ReadWriteLock;

/**
 * SPI for cache providers. 为缓存提供者的接口(SPI:串行外设接口。)
 * 

* One instance of cache will be created for each namespace. 每个命名空间都会创建一个缓存的实例对象 *

* The cache implementation must have a constructor that receives the cache id as an String parameter. * 继承{@link Cache}的实现类必须有一个可接收一个缓存ID的String类型参数的构造函数 *

* MyBatis will pass the namespace as id to the constructor. * Mybatis会将命名空间作为id传入构造方法 * *

 * public MyCache(final String id) {
 *  if (id == null) {
 *    throw new IllegalArgumentException("Cache instances require an ID");
 *  }
 *  this.id = id;
 *  initialize();
 * }
 * 
* @author Clinton Begin */ public interface Cache { /** * 获取缓存对象的唯一标识,一般是Mapper.xml的命名空间 * @return The identifier of this cache */ String getId(); /** * 保存key/value到缓存对象中 * @param key Can be any object but usually it is a {@link CacheKey} 可以是任何对象,但一般是CacheKey对象 * @param value The result of a select. 查询结果 */ void putObject(Object key, Object value); /** * 从缓存对象中获取key对应的value * @param key The key * @return The object stored in the cache. */ Object getObject(Object key); /** *

* 移除key对应的value *

* As of 3.3.0 this method is only called during a rollback * for any previous value that was missing in the cache. * This lets any blocking cache to release the lock that * may have previously put on the key. * A blocking cache puts a lock when a value is null * and releases it when the value is back again. * This way other threads will wait for the value to be * available instead of hitting the database. * 在3.3.0后,这个方法只是在回滚在缓存中丢失了的任何之前的值。 * 这使得任何一个阻塞缓存释放可能之前写入键的锁。 * 一个阻塞缓存当值为null时加一个锁,在值再次后台时释放这个所。 * 这样其他线程会等待可用的价值,而不是数据库。 * @param key The key * @return Not used */ Object removeObject(Object key); /** * Clears this cache instance. * 清空缓存 */ void clear(); /** * Optional. This method is not called by the core. * 获取缓存对象中存储的键/值对的数量 * @return The number of elements stored in the cache (not its capacity). */ int getSize(); /** * 获取读写锁,这个方法mybatis的所有Cache实现类都没有重写过,都是直接返回null *

* 从3.2.6起这个方法不再被框架核心调用,任何需要的锁,都必须由缓存供应商提供 *

* Optional. As of 3.2.6 this method is no longer called by the core. *

* Any locking needed by the cache must be provided internally by the cache provider. * * @return A ReadWriteLock */ ReadWriteLock getReadWriteLock(); }

PerpetualCache

/**
 *    Copyright 2009-2018 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.impl;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;

/**
 * 永久缓存 Cache接口实现类,里面就是维护着一个HashMap
 * 

* Cache接口只有这唯一一个基础实现,其他实现类全都是装饰模式持有另一个缓存对象 *

* @author Clinton Begin */ public class PerpetualCache implements Cache { /** * 缓存对象的唯一标识 */ private final String id; /** * 对象内部维护的HashMap,缓存Map */ private Map cache = new HashMap<>(); public PerpetualCache(String id) { this.id = id; } @Override public String getId() { return id; } @Override public int getSize() { return cache.size(); } @Override public void putObject(Object key, Object value) { cache.put(key, value); } @Override public Object getObject(Object key) { return cache.get(key); } @Override public Object removeObject(Object key) { return cache.remove(key); } @Override public void clear() { cache.clear(); } @Override public ReadWriteLock getReadWriteLock() { return null; } /** * 通过判断ID去实现相等,如果当前类型对象的ID属性为null会抛出{@link CacheException} */ @Override public boolean equals(Object o) { if (getId() == null) { throw new CacheException("Cache instances require an ID."); } //地址判断 if (this == o) { return true; } //是否是子类 if (!(o instanceof Cache)) { return false; } Cache otherCache = (Cache) o; //判断ID return getId().equals(otherCache.getId()); } /** * 以为属性ID的hashCode作为本类对象的hashCode,如果当前类型对象的ID属性为null会抛出{@link CacheException} */ @Override public int hashCode() { if (getId() == null) { throw new CacheException("Cache instances require an ID."); } return getId().hashCode(); } }

FifoCache

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.decorators;

import java.util.Deque;
import java.util.LinkedList;
import java.util.concurrent.locks.ReadWriteLock;

import org.apache.ibatis.cache.Cache;

/**
 * FIFO (first in, first out) cache decorator.
 * 使用先进先出缓存策略的缓存装饰类
 * @author Clinton Begin
 */
public class FifoCache implements Cache {

  /**
   * 委托Cache对象
   */
  private final Cache delegate;
  /**
   * 键列表
   * 

* Deque是Queue的子接口,我们知道Queue是一种队列形式,而Deque则是双向队列,它支持从两个端点方向检索和插入元素, * 因此Deque既可以支持LIFO形式也可以支持LIFO形式.Deque接口是一种比Stack和Vector更为丰富的抽象数据形式,因为它同时实现了以上两者. *

*

* 初始化时,该属性的实现类默认是{@link LinkedList} *

*/ private final Deque keyList; /** * 最大缓存数,初始化时为1024 */ private int size; public FifoCache(Cache delegate) { this.delegate = delegate; this.keyList = new LinkedList<>(); this.size = 1024; } /** * 取{@link #delegate}的ID */ @Override public String getId() { return delegate.getId(); } /** * 取{@link #delegate}的当前缓存数 */ @Override public int getSize() { return delegate.getSize(); } /** * 设置最大缓存数 * @param size */ public void setSize(int size) { this.size = size; } /** * 添加数据进缓存 * @param key Can be any object but usually it is a {@link @CacheKey} 可以是任何对象,但一般是CacheKey对象 * @param value The result of a select. 查询结果 */ @Override public void putObject(Object key, Object value) { cycleKeyList(key); delegate.putObject(key, value); } /** * 从{@link #delegate}中获取{@link @key}对应的缓存数据 */ @Override public Object getObject(Object key) { return delegate.getObject(key); } /** * 从{@link #delegate}中删除{@link @key}对应的缓存数据 */ @Override public Object removeObject(Object key) { return delegate.removeObject(key); } /** * 清空缓存,{@link #delegate},{@link #keyList}都会清空 */ @Override public void clear() { delegate.clear(); keyList.clear(); } /** * 获取读写锁,该类未实现读写锁,直接返回null * @return */ @Override public ReadWriteLock getReadWriteLock() { return null; } /** * 周期检查健名列表 *

* 将{@link @key}记录到{@link #keyList}中,再判断{@link #keyList}大小是否超过设定的最大值,超过就取出{@link #keyList}的 * 第一个元素赋值给{@link @oldestKey},然后删掉{@link #delegate}的{@link @oldestKey}缓存 *

* @param key */ private void cycleKeyList(Object key) { keyList.addLast(key); //检查当前keyList的大小是否大于设定的最大值 if (keyList.size() > size) { Object oldestKey = keyList.removeFirst();//移除列表中的第一个元素,并返回 delegate.removeObject(oldestKey);//移除对应的缓存 } } }

LruCache

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.decorators;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;

import org.apache.ibatis.cache.Cache;

/**
 * Lru (least recently used) cache decorator.
 * 使用最近最少使用的缓存策略的缓存装饰类
 * @author Clinton Begin
 */
public class LruCache implements Cache {

  /**
   * 真正存放缓存数据的Cache对象,委托类
   */
  private final Cache delegate;
  /**
   * 记录着每个key的访问次数
   */
  private Map keyMap;
  /**
   * 最近最少用的元素的key
   */
  private Object eldestKey;

  /**
   * 设置委托的Cache对象,并初始化最大缓存数,默认是1024
   * @param delegate
   */
  public LruCache(Cache delegate) {
    this.delegate = delegate;
    setSize(1024);
  }

  /**
   * 获取{@link #delegate}的ID
   * @return
   */
  @Override
  public String getId() {
    return delegate.getId();
  }

  /**
   * 获取{@link #delegate}的缓存数
   * @return
   */
  @Override
  public int getSize() {
    return delegate.getSize();
  }

  /**
   * 设置最大缓存数
   * 

* 每次调用该方法,都会重新新建一个LinkeHashMap实例对赋值给{@link #keyMap},这意思之前的keyMap所记录的每个元素的访问次数 * 都会丢失,重新记录访问次数。而{@link #eldestKey}并不会置空,还是会在每次添加缓存数据后,删除对应{@link #eldestKey}的 * {@link #delegate}里的元素。之所以这样设计,推测可能是因为设计者任务重新记录每个元素的访问次数并不会造成太大的业务问题,并且 * 既然重新调用了该方法,之前的扩容算法和初始化容量大小都应该都应该按照新的{@link @size}来重新设置,以保证性能的最佳 *

* @param size */ public void setSize(final int size) { /** * LinkedHashMap构造函数: * 第一个参数initialCapacity:初始化容量大小 * 第2个参数loadFactor:后面如果LinkedHashMap需要增大长度,按照capacity*loadFactor取整后增长 * 第3个参数accessOrder: * accessOrder设置为false,表示不是访问顺序而是插入顺序存储的,这也是默认值,表示LinkedHashMap中存储的顺序是按照调用put方法插入的顺序进行排序的 */ keyMap = new LinkedHashMap(size, .75F, true) { private static final long serialVersionUID = 4267176411845948333L; /** *

* 该方法是LinkedHashMap提供的一个钩子方法,是交给用户自己根据业务实现的。该方法会在添加 * 完元素后被调用,如果返回的是false,就不会删除最近最少使用的元素。默认是返回false。 *

*

* 如果大于{@link LruCache#keyMap}的大小大于设定的{@link #size}, * 就会返回true,并将最近最少使用的元素的key赋值给{@link LruCache#eldestKey} *

* @param eldest * @return */ @Override protected boolean removeEldestEntry(Map.Entry eldest) { boolean tooBig = size() > size; if (tooBig) { eldestKey = eldest.getKey();//记录最近最少使用的元素 } return tooBig; } }; } /** * 添加缓存数据 *

* 除了添加缓存数据到{@link #delegate}以外,还将{@link @key}加入到{@link #keyMap}中进行记录, * 并将将{@link #delegate}里最近最少用的元素删除。 *

* @param key Can be any object but usually it is a {@link @CacheKey} 可以是任何对象,但一般是CacheKey对象 * @param value The result of a select. 查询结果 */ @Override public void putObject(Object key, Object value) { delegate.putObject(key, value); // 将{@link @key}加入到{@link #keyMap}中进行记录,并将将{@link #delegate}里最近最少用的元素删除。 cycleKeyList(key); } /** * 获取缓存数据 *

* 除了从{@link #delegate}中取出对应{@link @key}的缓存数据,{@link @keyMap}也会对{@link @key}记录访问次数。 *

*/ @Override public Object getObject(Object key) { keyMap.get(key); //touch 这里会使得key在keyMap里的访问次数加1 return delegate.getObject(key); } /** * 删除{@link #delegate}的对应的{@link @key}的缓存数据 */ @Override public Object removeObject(Object key) { return delegate.removeObject(key); } /** * 清空{@link #delegate}和{@link #keyMap}的所有数据 */ @Override public void clear() { delegate.clear(); keyMap.clear(); } /** * 获取读写锁,该类未实现读写锁,直接返回null */ @Override public ReadWriteLock getReadWriteLock() { return null; } /** * 周期检查健名列表 *

* 将{@link @key}加入到{@link #keyMap}中进行记录,并将将{@link #delegate}里最近最少用的元素删除。 *

* @param key */ private void cycleKeyList(Object key) { keyMap.put(key, key); //将{@link #delegate}里最近最少用的元素删除。 if (eldestKey != null) { delegate.removeObject(eldestKey); eldestKey = null; } } }

SoftCache

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.decorators;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.Deque;
import java.util.LinkedList;
import java.util.concurrent.locks.ReadWriteLock;

import org.apache.ibatis.cache.Cache;

/**
 * Soft Reference cache decorator
 * Thanks to Dr. Heinz Kabutz for his guidance here.
 * 软引用回收策略 缓存装饰类
 * 

* 软引用只有当内存不足时才会被垃圾收集器回收。这里的实现机制中,使用了一个链表来保证一定数量的值即使内存不足也不会被回收, * 但是没有保存在该链表的值则有可能会被回收 *

* @author Clinton Begin */ public class SoftCache implements Cache { /** * 硬列表,以避免GC,初始化对象实例为LinkedList *

用于保存一定数量强引用的值

*/ private final Deque hardLinksToAvoidGarbageCollection; /** * 垃圾收集条目的队列 *

引用队列,当被垃圾收集器回收时,会将软引用对象放入此队列

*

* 当GC回收时,会将对象 *

*/ private final ReferenceQueue queueOfGarbageCollectedEntries; /** * 真正的缓存类,委托Cache对象 */ private final Cache delegate; /** * 保存强引用值的数量,初始化为256 */ private int numberOfHardLinks; public SoftCache(Cache delegate) { this.delegate = delegate; this.numberOfHardLinks = 256; this.hardLinksToAvoidGarbageCollection = new LinkedList<>(); this.queueOfGarbageCollectedEntries = new ReferenceQueue<>(); } /** * 取{@link #delegate}的ID */ @Override public String getId() { return delegate.getId(); } /** * 获取取{@link #delegate}的当前缓存数 */ @Override public int getSize() { removeGarbageCollectedItems(); return delegate.getSize(); } public void setSize(int size) { this.numberOfHardLinks = size; } @Override public void putObject(Object key, Object value) { // 移除被垃圾收集器回收的键值 removeGarbageCollectedItems(); // 将软引用作用到Value中 delegate.putObject(key, new SoftEntry(key, value, queueOfGarbageCollectedEntries)); } /** * 获取缓存数据,获取的数据如果不为null,会将这个数据放入强引用队列中,队列会判断当前可容纳的数量,超过了就采用先进先出的策略进行移除。 * @param key The key * @return 有可能返回null。因为GC可能已经清理相关数据 */ @Override public Object getObject(Object key) { Object result = null; @SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cache SoftReference softReference = (SoftReference) delegate.getObject(key); if (softReference != null) { result = softReference.get(); if (result == null) { // 该值被垃圾收集器回收,移除掉该项 delegate.removeObject(key); } else { /** * 这里以及下面的clear,想不通为什么要加hardLinksToAvoidGarbageCollection的同步?(在WeakCache中却没有加同步) * --我的推测:因为GC是一条线程,同步能使得主线程先执行下面代码后,GC才会工作,这样才能防止GC在主线程执行下面代码时 * 回收了对象。 */ // See #586 (and #335) modifications need more than a read lock synchronized (hardLinksToAvoidGarbageCollection) { //存入经常访问的键值到链表(最多256元素),防止垃圾回收 hardLinksToAvoidGarbageCollection.addFirst(result); if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) { // 超出容量,则移除最先保存的引用 //因为加入是加入到列表的第一个位置,随意最后一个就是最先加入的元素。 hardLinksToAvoidGarbageCollection.removeLast(); } } } } return result; } /** * 删除对应{@link @key}的缓存数据 * @param key The key * @return */ @Override public Object removeObject(Object key) { // 移除被垃圾收集器回收的键值 removeGarbageCollectedItems(); return delegate.removeObject(key); } /** * 清空缓存数据 *

* 清空强引用,移除被垃圾收集器回收的键值,清空缓存数据 *

*/ @Override public void clear() { //清空强引用 synchronized (hardLinksToAvoidGarbageCollection) { hardLinksToAvoidGarbageCollection.clear(); } // 移除被垃圾收集器回收的键值 removeGarbageCollectedItems(); //清空缓存数据 delegate.clear(); } @Override public ReadWriteLock getReadWriteLock() { return null; } /** * 删除被垃圾收集器回收的键值 */ private void removeGarbageCollectedItems() { SoftEntry sv; // 清空被垃圾收集器回收的value其相关联的键以及软引用 while ((sv = (SoftEntry) queueOfGarbageCollectedEntries.poll()) != null) { delegate.removeObject(sv.key); } } /** * 继承了{@link SoftReference},使得传进来的value转变成软引用。 *

* 这里将其Value作为软引用,而不是用key,因为Key不能被回收,如果被移除的话,就会影响到整个体系, * 最底层的实现使用HashMap实现的,没有Key,就没有办法移除相关的值。反过来,值被回收了,将软引用对象放到队列中, * 可以根据Key调用removeObject移除该关联的键和软引用对象。 *

*/ private static class SoftEntry extends SoftReference { /** * 保存与value相关联的Key,因为一旦被垃圾收集器回收,则此软引用对象会被放到关联的引用队列中, * 这样就可以根据Key,移除该键值对 */ private final Object key; SoftEntry(Object key, Object value, ReferenceQueue garbageCollectionQueue) { super(value, garbageCollectionQueue); this.key = key; } } }

WeakCache

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.decorators;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Deque;
import java.util.LinkedList;
import java.util.concurrent.locks.ReadWriteLock;

import org.apache.ibatis.cache.Cache;

/**
 * Weak Reference cache decorator.
 * Thanks to Dr. Heinz Kabutz for his guidance here.
 * 

* 弱引用回收策略 缓存装饰类,逻辑和{@link SoftCache}非常类似。就直接看{@link SoftCache}逻辑算了,我懒得写了 *

*

* 弱引用的对象一旦被垃圾收集器发现,则会被回收,无论内存是否足够 *

* @author Clinton Begin */ public class WeakCache implements Cache { /** * */ private final Deque hardLinksToAvoidGarbageCollection; private final ReferenceQueue queueOfGarbageCollectedEntries; private final Cache delegate; private int numberOfHardLinks; public WeakCache(Cache delegate) { this.delegate = delegate; this.numberOfHardLinks = 256; this.hardLinksToAvoidGarbageCollection = new LinkedList<>(); this.queueOfGarbageCollectedEntries = new ReferenceQueue<>(); } @Override public String getId() { return delegate.getId(); } @Override public int getSize() { removeGarbageCollectedItems(); return delegate.getSize(); } public void setSize(int size) { this.numberOfHardLinks = size; } @Override public void putObject(Object key, Object value) { removeGarbageCollectedItems(); delegate.putObject(key, new WeakEntry(key, value, queueOfGarbageCollectedEntries)); } @Override public Object getObject(Object key) { Object result = null; @SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cache WeakReference weakReference = (WeakReference) delegate.getObject(key); if (weakReference != null) { result = weakReference.get(); if (result == null) { delegate.removeObject(key); } else { hardLinksToAvoidGarbageCollection.addFirst(result); if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) { hardLinksToAvoidGarbageCollection.removeLast(); } } } return result; } @Override public Object removeObject(Object key) { removeGarbageCollectedItems(); return delegate.removeObject(key); } @Override public void clear() { hardLinksToAvoidGarbageCollection.clear(); removeGarbageCollectedItems(); delegate.clear(); } @Override public ReadWriteLock getReadWriteLock() { return null; } private void removeGarbageCollectedItems() { WeakEntry sv; while ((sv = (WeakEntry) queueOfGarbageCollectedEntries.poll()) != null) { delegate.removeObject(sv.key); } } private static class WeakEntry extends WeakReference { private final Object key; private WeakEntry(Object key, Object value, ReferenceQueue garbageCollectionQueue) { super(value, garbageCollectionQueue); this.key = key; } } }

LoggingCache

/**
 *    Copyright 2009-2017 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.decorators;

import java.util.concurrent.locks.ReadWriteLock;

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;

/**
 * 日志功能,装饰类,用于记录缓存的命中率,如果开启了DEBUG模式,则会输出命中率日志。
 * 

* 参考博客:https://www.cnblogs.com/jabnih/p/5705640.html *

* @author Clinton Begin */ public class LoggingCache implements Cache { /** * log的名字一般是命名空间,从{@link #delegate}的{@link Cache#getId()}方法获取。 */ private final Log log; private final Cache delegate; /** * 请求次数 */ protected int requests = 0; /** * 命中次数 */ protected int hits = 0; public LoggingCache(Cache delegate) { this.delegate = delegate; this.log = LogFactory.getLog(getId()); } @Override public String getId() { return delegate.getId(); } @Override public int getSize() { return delegate.getSize(); } @Override public void putObject(Object key, Object object) { delegate.putObject(key, object); } /** * 获取缓存数据,并统计请求次数和命中次数 * @param key The key * @return */ @Override public Object getObject(Object key) { requests++;//每次获取,请求次数+1 final Object value = delegate.getObject(key); if (value != null) { hits++;//获取的缓存数据时存在的,命中次数+1 } if (log.isDebugEnabled()) { log.debug("Cache Hit Ratio [" + getId() + "]: " + getHitRatio()); } return value; } @Override public Object removeObject(Object key) { return delegate.removeObject(key); } @Override public void clear() { delegate.clear(); } @Override public ReadWriteLock getReadWriteLock() { return null; } @Override public int hashCode() { return delegate.hashCode(); } @Override public boolean equals(Object obj) { return delegate.equals(obj); } /** * 获取命中率 *

* 命中率 = 命中次数 / 请求次数 *

* @return */ private double getHitRatio() { return (double) hits / (double) requests; } }

ScheduledCache

/**
 *    Copyright 2009-2017 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.decorators;

import java.util.concurrent.locks.ReadWriteLock;

import org.apache.ibatis.cache.Cache;

/**
 * 定时清空Cache,但是并没有开始一个定时任务,而是在使用Cache的时候,才去检查时间是否到了。
 * 

* 参考博客:https://www.cnblogs.com/jabnih/p/5705640.html *

* @author Clinton Begin */ public class ScheduledCache implements Cache { private final Cache delegate; /** * 清除缓存数据的时间间隔,初始化为1小时 */ protected long clearInterval; /** * 上一次清除的时间,初始化时赋值当前时间 */ protected long lastClear; public ScheduledCache(Cache delegate) { this.delegate = delegate; this.clearInterval = 60 * 60 * 1000; // 1 hour this.lastClear = System.currentTimeMillis(); } public void setClearInterval(long clearInterval) { this.clearInterval = clearInterval; } @Override public String getId() { return delegate.getId(); } @Override public int getSize() { clearWhenStale(); return delegate.getSize(); } @Override public void putObject(Object key, Object object) { clearWhenStale(); delegate.putObject(key, object); } @Override public Object getObject(Object key) { return clearWhenStale() ? null : delegate.getObject(key); } @Override public Object removeObject(Object key) { clearWhenStale(); return delegate.removeObject(key); } /** * 清空所有缓存数据 */ @Override public void clear() { lastClear = System.currentTimeMillis();//记录当前时间 delegate.clear(); } @Override public ReadWriteLock getReadWriteLock() { return null; } @Override public int hashCode() { return delegate.hashCode(); } @Override public boolean equals(Object obj) { return delegate.equals(obj); } /** * 判断是否到了清空数据的时间 */ private boolean clearWhenStale() { if (System.currentTimeMillis() - lastClear > clearInterval) { clear(); return true; } return false; } }

SerializedCache

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.decorators;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;
import java.util.concurrent.locks.ReadWriteLock;

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;
import org.apache.ibatis.io.Resources;

/**
 * 序列化功能,将值序列化后存到缓存中。该功能用于缓存返回一份实例的Copy,用于保存线程安全。
 * 

* 该缓存只是保存在内存里 *

*

* 参考博客:https://www.cnblogs.com/jabnih/p/5705640.html *

* @author Clinton Begin */ public class SerializedCache implements Cache { private final Cache delegate; public SerializedCache(Cache delegate) { this.delegate = delegate; } @Override public String getId() { return delegate.getId(); } @Override public int getSize() { return delegate.getSize(); } @Override public void putObject(Object key, Object object) { if (object == null || object instanceof Serializable) { // 先序列化后再存放到缓存中 delegate.putObject(key, serialize((Serializable) object)); } else { throw new CacheException("SharedCache failed to make a copy of a non-serializable object: " + object); } } @Override public Object getObject(Object key) { Object object = delegate.getObject(key); // 不为空,则反序列化,生成一份Copy return object == null ? null : deserialize((byte[]) object); } @Override public Object removeObject(Object key) { return delegate.removeObject(key); } @Override public void clear() { delegate.clear(); } @Override public ReadWriteLock getReadWriteLock() { return null; } @Override public int hashCode() { return delegate.hashCode(); } @Override public boolean equals(Object obj) { return delegate.equals(obj); } /** * 序列化 */ private byte[] serialize(Serializable value) { try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos)) { oos.writeObject(value); oos.flush(); return bos.toByteArray(); } catch (Exception e) { throw new CacheException("Error serializing object. Cause: " + e, e); } } // 反序列化 private Serializable deserialize(byte[] value) { Serializable result; try (ByteArrayInputStream bis = new ByteArrayInputStream(value); ObjectInputStream ois = new CustomObjectInputStream(bis)) { result = (Serializable) ois.readObject(); } catch (Exception e) { throw new CacheException("Error deserializing object. Cause: " + e, e); } return result; } public static class CustomObjectInputStream extends ObjectInputStream { public CustomObjectInputStream(InputStream in) throws IOException { super(in); } @Override protected Class resolveClass(ObjectStreamClass desc) throws ClassNotFoundException { // 此方法只有在待序列化的类第一次序列化的时候才会被调用 // 遍历所支持的ClassLoader,加载对应的Class return Resources.classForName(desc.getName()); } } }

SynchronizedCache

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.decorators;

import java.util.concurrent.locks.ReadWriteLock;

import org.apache.ibatis.cache.Cache;

/**
 * 同步Cache,实现比较简单,直接使用synchronized修饰方法。
 * @author Clinton Begin
 */
public class SynchronizedCache implements Cache {

  private final Cache delegate;

  public SynchronizedCache(Cache delegate) {
    this.delegate = delegate;
  }

  @Override
  public String getId() {
    return delegate.getId();
  }

  @Override
  public synchronized int getSize() {
    return delegate.getSize();
  }

  @Override
  public synchronized void putObject(Object key, Object object) {
    delegate.putObject(key, object);
  }

  @Override
  public synchronized Object getObject(Object key) {
    return delegate.getObject(key);
  }

  @Override
  public synchronized Object removeObject(Object key) {
    return delegate.removeObject(key);
  }

  @Override
  public synchronized void clear() {
    delegate.clear();
  }

  @Override
  public int hashCode() {
    return delegate.hashCode();
  }

  @Override
  public boolean equals(Object obj) {
    return delegate.equals(obj);
  }

  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }

}

TransactionalCache

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.decorators;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;

/**
 * The 2nd level cache transactional buffer.2级缓存事务缓冲区
 * 

* This class holds all cache entries that are to be added to the 2nd level cache during a Session. * Entries are sent to the cache when commit is called or discarded if the Session is rolled back. * Blocking cache support has been added. Therefore any get() that returns a cache miss * will be followed by a put() so any lock associated with the key can be released. * 这个类保留着所有在对话期间被添加到二级缓存的缓存数据。缓存数据在事务提交后会被传入二级缓存中,或者在事务会话回退后被丢弃。 * 阻塞着已经添加缓存的缓存数据,因此任何get()方法返回出来的缓存丢失,会随后将由put(),所以任何的和对应的key关联的锁可以被释放掉。 *

* 事务缓存,在提交的时候,才真正的放到Cache中,或者回滚的时候清除掉,对Cache没有影响 *

* @author Clinton Begin * @author Eduardo Macarron */ public class TransactionalCache implements Cache { private static final Log log = LogFactory.getLog(TransactionalCache.class); /** * 真正存放缓存数据的Cache对象,委托类 */ private final Cache delegate; /** * 是否在commit时清除二级缓存的标记 */ private boolean clearOnCommit; /** * 需要在commit时提交到二级缓存的数据 */ private final Map entriesToAddOnCommit; /** * 缓存未命中的数据,事务commit时,也会放入二级缓存(key,null) */ private final Set entriesMissedInCache; public TransactionalCache(Cache delegate) { this.delegate = delegate; this.clearOnCommit = false; this.entriesToAddOnCommit = new HashMap<>(); this.entriesMissedInCache = new HashSet<>(); } @Override public String getId() { return delegate.getId(); } @Override public int getSize() { return delegate.getSize(); } /** * 获取缓存数据 *

* 首先会在对应的二级缓存对象中查询,并将未命中的key记录到entiresMissedInCache中,之后根据clearOnCommit决定返回值 *

* @param key The key * @return */ @Override public Object getObject(Object key) { // issue #116 Object object = delegate.getObject(key); if (object == null) { entriesMissedInCache.add(key);//记录未命中的key } // issue #146 if (clearOnCommit) { return null; } else { return object; } } @Override public ReadWriteLock getReadWriteLock() { return null; } /** * 将没有提及的数据记录下来 * @param key Can be any object but usually it is a {@link @CacheKey} 可以是任何对象,但一般是CacheKey对象 * @param object */ @Override public void putObject(Object key, Object object) { entriesToAddOnCommit.put(key, object); } @Override public Object removeObject(Object key) { return null; } /** * 清空entriesToAddOnCommit,并且设置clearOnCommit为true */ @Override public void clear() { clearOnCommit = true; entriesToAddOnCommit.clear(); } public void commit() { if (clearOnCommit) { // 清空二级缓存 delegate.clear(); } //将数据刷新到二级缓存 flushPendingEntries(); //重置clearOnCommit为false,清空entriesToAddOnCommit、entriesMissedInCache集合 reset(); } /** * 回退 *

* 根据{@link #entriesMissedInCache},删除二级缓存的数据,重置clearOnCommit为false, * 清空entriesToAddOnCommit、entriesMissedInCache集合 *

*/ public void rollback() { //解锁丢失的缓存数据,使其根据{@link #entriesMissedInCache}删除二级缓存的数据 unlockMissedEntries(); //重置clearOnCommit为false,清空entriesToAddOnCommit、entriesMissedInCache集合 reset(); } /** * 重置一级缓存 *

* {@link #entriesToAddOnCommit}和{@link #entriesMissedInCache}全清空,而不清空二级缓存。 * 并将{@link #clearOnCommit}标记设置为false *

* */ private void reset() { clearOnCommit = false; entriesToAddOnCommit.clear(); entriesMissedInCache.clear(); } /** * 将数据刷新到二级缓存 *

* 将entriesToAddOnCommit和entriesMissedInCache添加到二级缓存,entriesMissedInCache只存储了key,所以添加到二级缓存时,value为null *

*/ private void flushPendingEntries() { for (Map.Entry entry : entriesToAddOnCommit.entrySet()) { delegate.putObject(entry.getKey(), entry.getValue()); } for (Object entry : entriesMissedInCache) { if (!entriesToAddOnCommit.containsKey(entry)) {//entriesMissedInCache只存储了key,所以添加到二级缓存时,value为null delegate.putObject(entry, null); } } } /** * 解锁丢失的缓存数据 *

* 删除在二级缓存中关于{@link #entriesMissedInCache}的所有元素的缓存数据,这里捕捉了所有移除缓存时的异常,保证每个缓存都能删除 *

*/ private void unlockMissedEntries() { for (Object entry : entriesMissedInCache) { //将try..catch放在循环里面,能够使得每个缓存数据都会执行删除,而不会导致有一个删除数据因为抛出异常,而导致其他缓存数据没有删除 try { delegate.removeObject(entry); } catch (Exception e) { log.warn("Unexpected exception while notifiying a rollback to the cache adapter." + "Consider upgrading your cache adapter to the latest version. Cause: " + e); } } } }

你可能感兴趣的:(Mybatis缓存策略源码)