MyBatis源码解析(四) Cache模块

936 阅读12分钟

MyBatis的二级缓存

MyBatis的缓存分为一级缓存和二级缓存,一级缓存是 SqlSession 级别的缓存,二级缓存是mapper级别的缓存。但是这篇博客主要是介绍mybaits中缓存接口和缓存键接口,以及一些缓存实现。

之前写过一篇博客简单介绍了一下Hibernate的两级缓存。

链接: https://blog.csdn.net/Letmetell_you/article/details/80876767

Cache接口

源码位置:org.apache.ibatis.cache.Cache

缓存容器接口,自定义操作方法,其他缓存实现类需要实现这个接口。

UML类图

Cache接口UML类图.png

上图中列出来的就是Cache接口的实现类,实现不同的缓存功能。

接口的源码很简单,就是定义了一些增删改查缓存的方法,和一个获取容器中缓存数量和获得读写锁的方法。我精简了下注释,源码内容如下。Cache接口其实是一个缓存容器,有点类似于一个HashMap(有一些实现类就是使用HashMap来保存操作缓存数据的)。

public interface Cache {

  /**
   * 获取标识
   */
  String getId();

  /**
   * 添加指定的键
   */
  void putObject(Object key, Object value);

  /**
   * 获取指定的键的值
   */
  Object getObject(Object key);

  /**
   * 删除指定的键的值
   */
  Object removeObject(Object key);

  /**
   * 清空缓存
   */
  void clear();

  /**
   * 获取容器中缓存的数量
   */
  int getSize();

  /**
   * 获得读写锁
   */
  default ReadWriteLock getReadWriteLock() {
    return null;
  }

}

PerpetualCache

源码位置:org.apache.ibatis.cache.impl.PerpetualCache

永不过期的缓存,使用HashMap来保存和操作数据,重写了 equals()hashCode() 方法,其他缓存操作都是直接调用的HashMap方法。

LoggingCache

源码位置:org.apache.ibatis.cache.decorators.LoggingCache

这是一个支持打印日志的 Cache 实现,代码也很简单,加了一些注释。

public class LoggingCache implements Cache {

  /**
   * mybaits log 对象
   */
  private final Log log;
  /**
   * 装饰的 Cache 对象
   */
  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);
  }

  @Override
  public Object getObject(Object key) {
    //请求次数加 1
    requests++;
    final Object value = delegate.getObject(key);
    if (value != null) {
      //命中缓存,命中次数加 1
      hits++;
    }
    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 int hashCode() {
    return delegate.hashCode();
  }

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

  /**
   * 计算命中比例
   * @return
   */
  private double getHitRatio() {
    //算法: 命中次数 / 请求缓存次数
    return (double) hits / (double) requests;
  }

}

BlockingCache

源码位置:org.apache.ibatis.cache.decorators.BlockingCache

阻塞的Cache实现类,这个实现不同的逻辑是在加锁上。当一个线程去获取缓存事,缓存不存在则会阻塞后续线程获取,当前线程则去添加缓存值,避免后续线程重复添加缓存。需要注意的是这个实现里 removeObject 方法并不是删除缓存值,而是移除锁。

public class BlockingCache implements Cache {

  /**
   * 阻塞等待超时时间
   */
  private long timeout;
  /**
   * 装饰的Cache对象
   */
  private final Cache delegate;
  /**
   * 缓存键 与  ReentrantLock 对象映射
   */
  private final ConcurrentHashMap<Object, ReentrantLock> locks;

  public BlockingCache(Cache delegate) {
    this.delegate = delegate;
    this.locks = new ConcurrentHashMap<>();
  }

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

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

  @Override
  public void putObject(Object key, Object value) {
    try {
      //添加缓存
      delegate.putObject(key, value);
    } finally {
      //释放锁
      releaseLock(key);
    }
  }

  @Override
  public Object getObject(Object key) {
    //获得锁
    acquireLock(key);
    //获得缓存执
    Object value = delegate.getObject(key);
    if (value != null) {
      //释放锁
      releaseLock(key);
    }
    return value;
  }

  @Override
  public Object removeObject(Object key) {
    // despite of its name, this method is called only to release locks
    //释放该键对应的锁
    releaseLock(key);
    return null;
  }

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

  /**
   * 获得 ReentrantLock 对象,如果不存在,则进行添加
   * @param key
   * @return
   */
  private ReentrantLock getLockForKey(Object key) {
    return locks.computeIfAbsent(key, k -> new ReentrantLock());
  }

  private void acquireLock(Object key) {
    //获得键对应的 ReentrantLock 对象
    Lock lock = getLockForKey(key);
    //获得锁,直到超时
    if (timeout > 0) {
      try {
        boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);
        if (!acquired) {
          throw new CacheException("Couldn't get a lock in " + timeout + " for the key " +  key + " at the cache " + delegate.getId());
        }
      } catch (InterruptedException e) {
        throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);
      }
    } else {
      //释放锁
      lock.lock();
    }
  }

  private void releaseLock(Object key) {
    //获得 ReentrantLock 对象
    ReentrantLock lock = locks.get(key);
    if (lock.isHeldByCurrentThread()) {
      //如果当前线程持有锁,进行释放
      lock.unlock();
    }
  }

  public long getTimeout() {
    return timeout;
  }

  public void setTimeout(long timeout) {
    this.timeout = timeout;
  }
}

SynchronizedCache

源码位置:org.apache.ibatis.cache.decorators.SynchronizedCache

同步Cache实现,内部也是使用装饰的Cache来实现缓存操作。不过这个实现在 getSize putObject getObject removeObject clear 这几个方法上添加了 synchronized关键字。

SerializedCache

源码位置:org.apache.ibatis.cache.decorators.SerializedCache

支持序列化值,其实就是在添加缓存的时候对值进行序列化,获取值的时候反序列化。

public class SerializedCache implements Cache {
  //装饰的 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);
      //取值进行反序列化
    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 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 {
      return Resources.classForName(desc.getName());
    }

  }

}

ScheduledCache

源码位置:org.apache.ibatis.cache.decorators.ScheduledCache

定时清空整个 Cache 的缓存,在每次操作缓存之前判断是否全部清空缓存

public class ScheduledCache implements Cache {

  /**
   * 装饰的 Cache 对象
   */
  private final Cache delegate;
  /**
   * 清空间隔,单位:毫秒
   */
  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 int hashCode() {
    return delegate.hashCode();
  }

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

  /**
   * 判断是否需要全部清空
   * @return
   */
  private boolean clearWhenStale() {
    if (System.currentTimeMillis() - lastClear > clearInterval) {
      //全部清空
      clear();
      return true;
    }
    return false;
  }

}

FifoCache

源码位置:org.apache.ibatis.cache.decorators.FifoCache

基于先进先出淘汰机制的Cache实现,此实现在删除缓存时并不会删除缓存key,所以旧的 key 也依旧会继续存在。

除此之外在添加key的时候该实现也不会去判断key是否已经存在,只会判断当前长度是否超过了队列上上限,所以重复添加就会在队列里存在多个相同的key,这个不能说是bug,只能说是允许重复的key,但是如果你在使用中发现同一个key拿到的缓存不止一个,可能就需要检查一下你使用的是不是 FifoCache 实现了。

public class FifoCache implements Cache {

  /**
   * 装饰的 Cache 对象
   */
  private final Cache delegate;
  /**
   * 双端队列,记录键值的添加
   */
  private final Deque<Object> keyList;
  /**
   * 队列上限
   */
  private int size;

  public FifoCache(Cache delegate) {
    this.delegate = delegate;
    this.keyList = new LinkedList<>();
    this.size = 1024;
  }

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

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

  public void setSize(int size) {
    this.size = size;
  }

  @Override
  public void putObject(Object key, Object value) {
    //循环 keyList
    cycleKeyList(key);
    delegate.putObject(key, value);
  }

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

  @Override
  public Object removeObject(Object key) {
    //删除缓存,未清空keyList中的数据
    return delegate.removeObject(key);
  }

  @Override
  public void clear() {
    //清空缓存时,同时清空维护的 keyList
    delegate.clear();
    keyList.clear();
  }

  private void cycleKeyList(Object key) {
    //添加到 keyList
    keyList.addLast(key);
    if (keyList.size() > size) {
      //如果添加新 key 之后 ketList 的长度大于 队列上线,将队列的首位移除
      Object oldestKey = keyList.removeFirst();
      delegate.removeObject(oldestKey);
    }
  }

}

LruCache

源码位置:org.apache.ibatis.cache.decorators.LruCache

此实现是基于最少使用的淘汰机制的 Cache 实现,简单说就是当添加缓存时发现已经达到上限的时候,淘汰掉最少使用的 key 以及对应的缓存。使用 LinkedHashMap 的淘汰机制,具体源码的解析请参考下面的源码。

public class LruCache implements Cache {

  /**
   * 装饰的 Cache 对象
   */
  private final Cache delegate;
  /**
   * 基于 LinkedHashMap 实现淘汰机制
   */
  private Map<Object, Object> keyMap;
  /**
   * 最老/最少被使用的键,即将被淘汰的 key
   */
  private Object eldestKey;

  public LruCache(Cache delegate) {
    this.delegate = delegate;
    //初始化 keyMap 对象
    setSize(1024);
  }

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

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

  /**
   * 初始化keyMap,但是此方法的权限标识是 public ,也就是说可以通过这个方法来指定 keyMap的长度,默认是1024,可以根据自己的需求来变更
   * @param size
   */
  public void setSize(final int size) {
    //LinkedHasmp的一个构造函数,当参数 accessOrder 为 true 时,即会按照访问的顺序排序,最近访问的在最前,最早访问的在最后
    keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
      private static final long serialVersionUID = 4267176411845948333L;

      @Override
      protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
        //重写 LinkedHashMapLinkedHashMap 的删除元素方法,当满足一定条件的时候删除元素,LinkedHashMap中默认是不删除
        //这里判断的条件就是 keyMap 长度大于初始化 keyMap 时给定的值,满足条件时将最少使用的 key 设置为待删除,
        // 等下次添加新key的时候判断 eldestKey 参数不为空则对 eldestKey 进行移除
        boolean tooBig = size() > size;
        if (tooBig) {
          eldestKey = eldest.getKey();
        }
        return tooBig;
      }
    };
  }

  @Override
  public void putObject(Object key, Object value) {
    delegate.putObject(key, value);
    cycleKeyList(key);
  }

  @Override
  public Object getObject(Object key) {
    keyMap.get(key); //touch
    return delegate.getObject(key);
  }

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

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

  private void cycleKeyList(Object key) {
    //添加 key 到keyMap中
    keyMap.put(key, key);
    //如果超过上限,则删除最少使用的可以
    if (eldestKey != null) {
      //移除 eldestKey
      delegate.removeObject(eldestKey);
      //置空
      eldestKey = null;
    }
  }

}

WeakCache

源码位置:org.apache.ibatis.cache.decorators.WeakCache

基于 java.lang.ref.WeakReference 的Cache实现类,主要是基于内部维护的一个强引用和mybatis基于 java.lang.ref.WeakReference 扩展的WeakEntry来实现缓存淘汰。在这个实现里,一样存在key多次添加会导致重复key的问题。

public class WeakCache implements Cache {
  //强引用的键的队列
  private final Deque<Object> hardLinksToAvoidGarbageCollection;
  //被GC回收的 WeakEntry 集合,避免被 GC
  private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
  //装饰的 Cache 对象
  private final Cache delegate;
  //hardLinksToAvoidGarbageCollection 的大小
  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() {
    //移除已经被 GC 回收的WeakEntry
    removeGarbageCollectedItems();
    return delegate.getSize();
  }

  public void setSize(int size) {
    this.numberOfHardLinks = size;
  }

  @Override
  public void putObject(Object key, Object value) {
    //移除已经被 GC 回收的 WeakEntry
    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<Object> weakReference = (WeakReference<Object>) delegate.getObject(key);
    if (weakReference != null) {
      //获得值
      result = weakReference.get();
      if (result == null) {
        //值为空,表示已经被GC回收,移除缓存
        delegate.removeObject(key);
      } else {
        //非空,添加到 hardLinksToAvoidGarbageCollection 队列首部,未做key唯一性判断,所以存在重复添加的情况 ,避免被 GC
        hardLinksToAvoidGarbageCollection.addFirst(result);
        if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {
          //如果长度超出上限,则移除队列尾部的元素
          hardLinksToAvoidGarbageCollection.removeLast();
        }
      }
    }
    return result;
  }

  @Override
  public Object removeObject(Object key) {
    //移除已经被 GC 回收的 WeakEntry
    removeGarbageCollectedItems();
    //移除缓存
    return delegate.removeObject(key);
  }

  @Override
  public void clear() {
    //清空 hardLinksToAvoidGarbageCollection
    hardLinksToAvoidGarbageCollection.clear();
    //移除已经被 GC 回收的 WeakEntry
    removeGarbageCollectedItems();
    //清空缓存
    delegate.clear();
  }

  /**
   * 移除已经被 GC 回收的键
   */
  private void removeGarbageCollectedItems() {
    WeakEntry sv;
    while ((sv = (WeakEntry) queueOfGarbageCollectedEntries.poll()) != null) {
      delegate.removeObject(sv.key);
    }
  }

  /**
   * 继承自 WeakReference ,增加缓存key属性,
   */
  private static class WeakEntry extends WeakReference<Object> {
    //键
    private final Object key;

    private WeakEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
      super(value, garbageCollectionQueue);
      this.key = key;
    }
  }

}

SoftCache

源码位置:org.apache.ibatis.cache.decorators.SoftCache

基于java.lang.ref.SoftReference 的Cache实现,SoftCache内部实现了SoftEntry,其他基本上与WeakCache基本上是差不多的,只是在一些操作中会对 hardLinksToAvoidGarbageCollection 加锁。

public class WeakCache implements Cache {
  //强引用的键的队列
  private final Deque<Object> hardLinksToAvoidGarbageCollection;
  //被GC回收的 WeakEntry 集合,避免被 GC
  private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
  //装饰的 Cache 对象
  private final Cache delegate;
  //hardLinksToAvoidGarbageCollection 的大小
  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() {
    //移除已经被 GC 回收的WeakEntry
    removeGarbageCollectedItems();
    return delegate.getSize();
  }

  public void setSize(int size) {
    this.numberOfHardLinks = size;
  }

  @Override
  public void putObject(Object key, Object value) {
    //移除已经被 GC 回收的 WeakEntry
    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<Object> weakReference = (WeakReference<Object>) delegate.getObject(key);
    if (weakReference != null) {
      //获得值
      result = weakReference.get();
      if (result == null) {
        //值为空,表示已经被GC回收,移除缓存
        delegate.removeObject(key);
      } else {
        //非空,添加到 hardLinksToAvoidGarbageCollection 队列首部,未做key唯一性判断,所以存在重复添加的情况 ,避免被 GC
        hardLinksToAvoidGarbageCollection.addFirst(result);
        if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {
          //如果长度超出上限,则移除队列尾部的元素
          hardLinksToAvoidGarbageCollection.removeLast();
        }
      }
    }
    return result;
  }

  @Override
  public Object removeObject(Object key) {
    //移除已经被 GC 回收的 WeakEntry
    removeGarbageCollectedItems();
    //移除缓存
    return delegate.removeObject(key);
  }

  @Override
  public void clear() {
    //清空 hardLinksToAvoidGarbageCollection
    hardLinksToAvoidGarbageCollection.clear();
    //移除已经被 GC 回收的 WeakEntry
    removeGarbageCollectedItems();
    //清空缓存
    delegate.clear();
  }

  /**
   * 移除已经被 GC 回收的键
   */
  private void removeGarbageCollectedItems() {
    WeakEntry sv;
    while ((sv = (WeakEntry) queueOfGarbageCollectedEntries.poll()) != null) {
      delegate.removeObject(sv.key);
    }
  }

  /**
   * 继承自 WeakReference ,增加缓存key属性,
   */
  private static class WeakEntry extends WeakReference<Object> {
    //键
    private final Object key;

    private WeakEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
      super(value, garbageCollectionQueue);
      this.key = key;
    }
  }

}

CachKey

源码位置:org.apache.ibatis.cache.CacheKey

mybaits中的缓存键,不只是单纯的String字符串,而是由多个对象组成,共同计算缓存键。CacheKey中封装了多个影响缓存的属性。

public class CacheKey implements Cloneable, Serializable {

  private static final long serialVersionUID = 1146682552656046210L;

  //单例 空缓存键
  public static final CacheKey NULL_CACHE_KEY = new NullCacheKey();

  //multiplier 的值
  private static final int DEFAULT_MULTIPLYER = 37;
  //hashcode 的值
  private static final int DEFAULT_HASHCODE = 17;

  //hashcode求值的系数
  private final int multiplier;
  //缓存键的hashcode
  private int hashcode;
  //校验和
  private long checksum;
  //updateList 的长度
  private int count;
  // 8/21/2017 - Sonarlint flags this as needing to be marked transient.  While true if content is not serializable, this is not always true and thus should not be marked transient.
  //计算 hashcode 的对象的集合
  private List<Object> updateList;

  public CacheKey() {
    this.hashcode = DEFAULT_HASHCODE;
    this.multiplier = DEFAULT_MULTIPLYER;
    this.count = 0;
    this.updateList = new ArrayList<>();
  }

  public CacheKey(Object[] objects) {
    this();
    //基于 objects ,更新相关属性
    updateAll(objects);
  }

  public int getUpdateCount() {
    return updateList.size();
  }

  public void update(Object object) {
    //方法参数 object 的 hashcode
    int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);

    count++;
    //checksum 为 baseHashCode 求和
    checksum += baseHashCode;
    //计算hashcode值
    baseHashCode *= count;
    hashcode = multiplier * hashcode + baseHashCode;
    //添加 object(缓存key) 到updateList中
    updateList.add(object);
  }

  public void updateAll(Object[] objects) {
    //遍历 objects 数组,调用 update 方法,更新相关属性
    for (Object o : objects) {
      update(o);
    }
  }

  @Override
  public boolean equals(Object object) {
    if (this == object) {
      return true;
    }
    if (!(object instanceof CacheKey)) {
      return false;
    }

    final CacheKey cacheKey = (CacheKey) object;

    if (hashcode != cacheKey.hashcode) {
      return false;
    }
    if (checksum != cacheKey.checksum) {
      return false;
    }
    if (count != cacheKey.count) {
      return false;
    }

    for (int i = 0; i < updateList.size(); i++) {
      Object thisObject = updateList.get(i);
      Object thatObject = cacheKey.updateList.get(i);
      if (!ArrayUtil.equals(thisObject, thatObject)) {
        return false;
      }
    }
    return true;
  }

  @Override
  public int hashCode() {
    return hashcode;
  }

  @Override
  public String toString() {
    StringJoiner returnValue = new StringJoiner(":");
    returnValue.add(String.valueOf(hashcode));
    returnValue.add(String.valueOf(checksum));
    updateList.stream().map(ArrayUtil::toString).forEach(returnValue::add);
    return returnValue.toString();
  }

  @Override
  public CacheKey clone() throws CloneNotSupportedException {
    //克隆 CacheKey 对象
    CacheKey clonedCacheKey = (CacheKey) super.clone();
    //创建 updateList 数组,避免原数组修改
    clonedCacheKey.updateList = new ArrayList<>(updateList);
    return clonedCacheKey;
  }
    
}    

NullCacheKey

源码位置:org.apache.ibatis.cache.NullCacheKey

继承自 CacheKey ,空缓存键。

public final class NullCacheKey extends CacheKey {

  private static final long serialVersionUID = 3704229911977019465L;

  public NullCacheKey() {
    super();
  }

  @Override
  public void update(Object object) {
    throw new CacheException("Not allowed to update a NullCacheKey instance.");
  }

  @Override
  public void updateAll(Object[] objects) {
    throw new CacheException("Not allowed to update a NullCacheKey instance.");
  }
}public final class NullCacheKey extends CacheKey {

  private static final long serialVersionUID = 3704229911977019465L;

  public NullCacheKey() {
    super();
  }

  @Override
  public void update(Object object) {
    throw new CacheException("Not allowed to update a NullCacheKey instance.");
  }

  @Override
  public void updateAll(Object[] objects) {
    throw new CacheException("Not allowed to update a NullCacheKey instance.");
  }
}

谈谈理想.jpg