apache缓存清理_深挖 Mybatis 源码:缓存模块
作者:AmyliaY
出自:Doocs開源社區(qū)
原文:my.oschina.net/doocs/blog/4549852
MyBatis 中的緩存分為一級(jí)緩存、二級(jí)緩存,但在本質(zhì)上是相同的,它們使用的都是 Cache 接口的實(shí)現(xiàn)。在這篇文章里,我們就來分析 Cache 接口以及多個(gè)實(shí)現(xiàn)類的具體實(shí)現(xiàn)。
1、Cache 組件
MyBatis 中緩存模塊相關(guān)的代碼位于 org.apache.ibatis.cache 包 下,其中 Cache 接口 是緩存模塊中最核心的接口,它定義了所有緩存的基本行為。
public interface Cache {/*** 獲取當(dāng)前緩存的 Id*/String getId();/*** 存入緩存的 key 和 value,key 一般為 CacheKey對(duì)象*/void putObject(Object key, Object value);/*** 根據(jù) key 獲取緩存值*/Object getObject(Object key);/*** 刪除指定的緩存項(xiàng)*/Object removeObject(Object key);/*** 清空緩存*/void clear();/*** 獲取緩存的大小*/int getSize();/*** !!!!!!!!!!!!!!!!!!!!!!!!!!* 獲取讀寫鎖,可以看到,這個(gè)接口方法提供了默認(rèn)的實(shí)現(xiàn)!!* 這是 Java8 的新特性!!只是平時(shí)開發(fā)時(shí)很少用到!!!* !!!!!!!!!!!!!!!!!!!!!!!!!!*/default ReadWriteLock getReadWriteLock() {return null;} }如下圖所示,Cache 接口 的實(shí)現(xiàn)類有很多,但大部分都是裝飾器,只有 PerpetualCache 提供了 Cache 接口 的基本實(shí)現(xiàn)。
PerpetualCache
PerpetualCache(Perpetual:永恒的,持續(xù)的)在緩存模塊中扮演著被裝飾的角色,其實(shí)現(xiàn)比較簡(jiǎn)單,底層使用 HashMap 記錄緩存項(xiàng),也是通過該 HashMap 對(duì)象 的方法實(shí)現(xiàn)的 Cache 接口 中定義的相應(yīng)方法。
public class PerpetualCache implements Cache {// Cache對(duì)象 的唯一標(biāo)識(shí)private final String id;// 其所有的緩存功能實(shí)現(xiàn),都是基于 JDK 的 HashMap 提供的方法private Map<Object, Object> cache = new HashMap<>();public PerpetualCache(String id) {this.id = id;}@Overridepublic String getId() {return id;}@Overridepublic int getSize() {return cache.size();}@Overridepublic void putObject(Object key, Object value) {cache.put(key, value);}@Overridepublic Object getObject(Object key) {return cache.get(key);}@Overridepublic Object removeObject(Object key) {return cache.remove(key);}@Overridepublic void clear() {cache.clear();}/*** 其重寫了 Object 中的 equals() 和 hashCode()方法,兩者都只關(guān)心 id字段*/@Overridepublic 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;return getId().equals(otherCache.getId());}@Overridepublic int hashCode() {if (getId() == null) {throw new CacheException("Cache instances require an ID.");}return getId().hashCode();} }下面來看一下 cache.decorators 包 下提供的裝飾器,它們都直接實(shí)現(xiàn)了 Cache 接口,扮演著裝飾器的角色。這些裝飾器會(huì)在 PerpetualCache 的基礎(chǔ)上提供一些額外的功能,通過多個(gè)組合后滿足一個(gè)特定的需求。
BlockingCache
BlockingCache 是阻塞版本的緩存裝飾器,它會(huì)保證只有一個(gè)線程到數(shù)據(jù)庫(kù)中查找指定 key 對(duì)應(yīng)的數(shù)據(jù)。
public class BlockingCache implements Cache {// 阻塞超時(shí)時(shí)長(zhǎng)private long timeout;// 持有的被裝飾者private final Cache delegate;// 每個(gè) key 都有其對(duì)應(yīng)的 ReentrantLock鎖對(duì)象private final ConcurrentHashMap<Object, ReentrantLock> locks;// 初始化 持有的持有的被裝飾者 和 鎖集合public BlockingCache(Cache delegate) {this.delegate = delegate;this.locks = new ConcurrentHashMap<>();} }假設(shè) 線程 A 在 BlockingCache 中未查找到 keyA 對(duì)應(yīng)的緩存項(xiàng)時(shí),線程 A 會(huì)獲取 keyA 對(duì)應(yīng)的鎖,這樣,線程 A 在后續(xù)查找 keyA 時(shí),其它線程會(huì)被阻塞。
// 根據(jù) key 獲取鎖對(duì)象,然后上鎖private void acquireLock(Object key) {// 獲取 key 對(duì)應(yīng)的鎖對(duì)象Lock lock = getLockForKey(key);// 獲取鎖,帶超時(shí)時(shí)長(zhǎng)if (timeout > 0) {try {boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);if (!acquired) { // 超時(shí),則拋出異常throw new CacheException("Couldn't get a lock in " + timeout + " for the key " + key + " at the cache " + delegate.getId());}} catch (InterruptedException e) {// 如果獲取鎖失敗,則阻塞一段時(shí)間throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);}} else {// 上鎖lock.lock();}}private ReentrantLock getLockForKey(Object key) {// Java8 新特性,Map系列類 中新增的方法// V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)// 表示,若 key 對(duì)應(yīng)的 value 為空,則將第二個(gè)參數(shù)的返回值存入該 Map集合 并返回return locks.computeIfAbsent(key, k -> new ReentrantLock());}假設(shè) 線程 A 從數(shù)據(jù)庫(kù)中查找到 keyA 對(duì)應(yīng)的結(jié)果對(duì)象后,將結(jié)果對(duì)象放入到 BlockingCache 中,此時(shí) 線程 A 會(huì)釋放 keyA 對(duì)應(yīng)的鎖,喚醒阻塞在該鎖上的線程。其它線程即可從 BlockingCache 中獲取 keyA 對(duì)應(yīng)的數(shù)據(jù),而不是再次訪問數(shù)據(jù)庫(kù)。
@Overridepublic void putObject(Object key, Object value) {try {// 存入 key 和其對(duì)應(yīng)的緩存項(xiàng)delegate.putObject(key, value);} finally {// 最后釋放鎖releaseLock(key);}}private void releaseLock(Object key) {ReentrantLock lock = locks.get(key);// 鎖是否被當(dāng)前線程持有if (lock.isHeldByCurrentThread()) {// 是,則釋放鎖lock.unlock();}}FifoCache 和 LruCache
在很多場(chǎng)景中,為了控制緩存的大小,系統(tǒng)需要按照一定的規(guī)則清理緩存。FifoCache 是先入先出版本的裝飾器,當(dāng)向緩存添加數(shù)據(jù)時(shí),如果緩存項(xiàng)的個(gè)數(shù)已經(jīng)達(dá)到上限,則會(huì)將緩存中最老(即最早進(jìn)入緩存)的緩存項(xiàng)刪除。
public class FifoCache implements Cache {// 被裝飾對(duì)象private final Cache delegate;// 用一個(gè) FIFO 的隊(duì)列記錄 key 的順序,其具體實(shí)現(xiàn)為 LinkedListprivate final Deque<Object> keyList;// 決定了緩存的容量上限private int size;// 國(guó)際慣例,通過構(gòu)造方法初始化自己的屬性,緩存容量上限默認(rèn)為 1024個(gè)public FifoCache(Cache delegate) {this.delegate = delegate;this.keyList = new LinkedList<>();this.size = 1024;}@Overridepublic String getId() {return delegate.getId();}@Overridepublic int getSize() {return delegate.getSize();}public void setSize(int size) {this.size = size;}@Overridepublic void putObject(Object key, Object value) {// 存儲(chǔ)緩存項(xiàng)之前,先在 keyList 中注冊(cè)cycleKeyList(key);// 存儲(chǔ)緩存項(xiàng)delegate.putObject(key, value);}private void cycleKeyList(Object key) {// 在 keyList隊(duì)列 中注冊(cè)要添加的 keykeyList.addLast(key);// 如果注冊(cè)這個(gè) key 會(huì)超出容積上限,則把最老的一個(gè)緩存項(xiàng)清除掉if (keyList.size() > size) {Object oldestKey = keyList.removeFirst();delegate.removeObject(oldestKey);}}@Overridepublic Object getObject(Object key) {return delegate.getObject(key);}@Overridepublic Object removeObject(Object key) {return delegate.removeObject(key);}// 除了清理緩存項(xiàng),還要清理 key 的注冊(cè)列表@Overridepublic void clear() {delegate.clear();keyList.clear();} }LruCache 是按照"近期最少使用算法"(Least Recently Used, LRU)進(jìn)行緩存清理的裝飾器,在需要清理緩存時(shí),它會(huì)清除最近最少使用的緩存項(xiàng)。
public class LruCache implements Cache {// 被裝飾者private final Cache delegate;// 這里使用的是 LinkedHashMap,它繼承了 HashMap,但它的元素是有序的private Map<Object, Object> keyMap;// 最近最少被使用的緩存項(xiàng)的 keyprivate Object eldestKey;// 國(guó)際慣例,構(gòu)造方法中進(jìn)行屬性初始化public LruCache(Cache delegate) {this.delegate = delegate;// 這里初始化了 keyMap,并定義了 eldestKey 的取值規(guī)則setSize(1024);}public void setSize(final int size) {// 初始化 keyMap,同時(shí)指定該 Map 的初始容積及加載因子,第三個(gè)參數(shù)true 表示 該LinkedHashMap// 記錄的順序是 accessOrder,即,LinkedHashMap.get()方法 會(huì)改變其中元素的順序keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {private static final long serialVersionUID = 4267176411845948333L;// 當(dāng)調(diào)用 LinkedHashMap.put()方法 時(shí),該方法會(huì)被調(diào)用@Overrideprotected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {boolean tooBig = size() > size;if (tooBig) {// 當(dāng)已達(dá)到緩存上限,更新 eldestKey字段,后面將其刪除eldestKey = eldest.getKey();}return tooBig;}};}// 存儲(chǔ)緩存項(xiàng)@Overridepublic void putObject(Object key, Object value) {delegate.putObject(key, value);// 記錄緩存項(xiàng)的 key,超出容量則清除最久未使用的緩存項(xiàng)cycleKeyList(key);}private void cycleKeyList(Object key) {keyMap.put(key, key);// eldestKey 不為空,則表示已經(jīng)達(dá)到緩存上限if (eldestKey != null) {// 清除最久未使用的緩存delegate.removeObject(eldestKey);// 制空eldestKey = null;}}@Overridepublic Object getObject(Object key) {// 訪問 key元素 會(huì)改變?cè)撛卦?LinkedHashMap 中的順序keyMap.get(key); //touchreturn delegate.getObject(key);}@Overridepublic String getId() {return delegate.getId();}@Overridepublic int getSize() {return delegate.getSize();}@Overridepublic Object removeObject(Object key) {return delegate.removeObject(key);}@Overridepublic void clear() {delegate.clear();keyMap.clear();} }SoftCache 和 WeakCache
在分析 SoftCache 和 WeakCache 實(shí)現(xiàn)之前,我們?cè)贉亓?xí)一下 Java 提供的 4 種引用類型,強(qiáng)引用 StrongReference、軟引用 SoftReference、弱引用 WeakReference 和虛引用 PhantomReference。
?強(qiáng)引用 平時(shí)用的最多的,如 Object obj = new Object(),新建的 Object 對(duì)象 就是被強(qiáng)引用的。如果一個(gè)對(duì)象被強(qiáng)引用,即使是 JVM 內(nèi)存空間不足,要拋出 OutOfMemoryError 異常,GC 也絕不會(huì)回收該對(duì)象。?軟引用 僅次于強(qiáng)引用的一種引用,它使用類 SoftReference 來表示。當(dāng) JVM 內(nèi)存不足時(shí),GC 會(huì)回收那些只被軟引用指向的對(duì)象,從而避免內(nèi)存溢出。軟引用適合引用那些可以通過其他方式恢復(fù)的對(duì)象,例如, 數(shù)據(jù)庫(kù)緩存中的對(duì)象就可以從數(shù)據(jù)庫(kù)中恢復(fù),所以軟引用可以用來實(shí)現(xiàn)緩存,下面要介紹的 SoftCache 就是通過軟引用實(shí)現(xiàn)的。
另外,由于在程序使用軟引用之前的某個(gè)時(shí)刻,其所指向的對(duì)象可能己經(jīng)被 GC 回收掉了,所以通過 Reference.get()方法 來獲取軟引用所指向的對(duì)象時(shí),總是要通過檢查該方法返回值是否為 null,來判斷被軟引用的對(duì)象是否還存活。?弱引用 弱引用使用 WeakReference 表示,它不會(huì)阻止所引用的對(duì)象被 GC 回收。在 JVM 進(jìn)行垃圾回收時(shí),如果指向一個(gè)對(duì)象的所有引用都是弱引用,那么該對(duì)象會(huì)被回收。所以,只被弱引用所指向的對(duì)象,其生存周期是 兩次 GC 之間 的這段時(shí)間,而只被軟引用所指向的對(duì)象可以經(jīng)歷多次 GC,直到出現(xiàn)內(nèi)存緊張的情況才被回收。?虛引用 最弱的一種引用類型,由類 PhantomReference 表示。虛引用可以用來實(shí)現(xiàn)比較精細(xì)的內(nèi)存使用控制,但很少使用。?引用隊(duì)列(ReferenceQueue ) 很多場(chǎng)景下,我們的程序需要在一個(gè)對(duì)象被 GC 時(shí)得到通知,引用隊(duì)列就是用于收集這些信息的隊(duì)列。在創(chuàng)建 SoftReference 對(duì)象 時(shí),可以為其關(guān)聯(lián)一個(gè)引用隊(duì)列,當(dāng) SoftReference 所引用的對(duì)象被 GC 時(shí), JVM 就會(huì)將該 SoftReference 對(duì)象 添加到與之關(guān)聯(lián)的引用隊(duì)列中。當(dāng)需要檢測(cè)這些通知信息時(shí),就可以從引用隊(duì)列中獲取這些 SoftReference 對(duì)象。不僅是 SoftReference,弱引用和虛引用都可以關(guān)聯(lián)相應(yīng)的隊(duì)列。
現(xiàn)在來看一下 SoftCache 的具體實(shí)現(xiàn)。
public class SoftCache implements Cache {// 這里使用了 LinkedList 作為容器,在 SoftCache 中,最近使用的一部分緩存項(xiàng)不會(huì)被 GC// 這是通過將其 value 添加到 hardLinksToAvoidGarbageCollection集合 實(shí)現(xiàn)的(即,有強(qiáng)引用指向其value)private final Deque<Object> hardLinksToAvoidGarbageCollection;// 引用隊(duì)列,用于記錄已經(jīng)被 GC 的緩存項(xiàng)所對(duì)應(yīng)的 SoftEntry對(duì)象private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;// 持有的被裝飾者private final Cache delegate;// 強(qiáng)連接的個(gè)數(shù),默認(rèn)為 256private int numberOfHardLinks;// 構(gòu)造方法進(jìn)行屬性的初始化public SoftCache(Cache delegate) {this.delegate = delegate;this.numberOfHardLinks = 256;this.hardLinksToAvoidGarbageCollection = new LinkedList<>();this.queueOfGarbageCollectedEntries = new ReferenceQueue<>();}private static class SoftEntry extends SoftReference<Object> {private final Object key;SoftEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {// 指向 value 的引用是軟引用,并且關(guān)聯(lián)了 引用隊(duì)列super(value, garbageCollectionQueue);// 強(qiáng)引用this.key = key;}}@Overridepublic void putObject(Object key, Object value) {// 清除已經(jīng)被 GC 的緩存項(xiàng)removeGarbageCollectedItems();// 添加緩存delegate.putObject(key, new SoftEntry(key, value, queueOfGarbageCollectedEntries));}private void removeGarbageCollectedItems() {SoftEntry sv;// 遍歷 queueOfGarbageCollectedEntries集合,清除已經(jīng)被 GC 的緩存項(xiàng) valuewhile ((sv = (SoftEntry) queueOfGarbageCollectedEntries.poll()) != null) {delegate.removeObject(sv.key);}}@Overridepublic Object getObject(Object key) {Object result = null;@SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cache// 用一個(gè)軟引用指向 key 對(duì)應(yīng)的緩存項(xiàng)SoftReference<Object> softReference = (SoftReference<Object>) delegate.getObject(key);// 檢測(cè)緩存中是否有對(duì)應(yīng)的緩存項(xiàng)if (softReference != null) {// 獲取 softReference 引用的 valueresult = softReference.get();// 如果 softReference 引用的對(duì)象已經(jīng)被 GC,則從緩存中清除對(duì)應(yīng)的緩存項(xiàng)if (result == null) {delegate.removeObject(key);} else {synchronized (hardLinksToAvoidGarbageCollection) {// 將緩存項(xiàng)的 value 添加到 hardLinksToAvoidGarbageCollection集合 中保存hardLinksToAvoidGarbageCollection.addFirst(result);// 如果 hardLinksToAvoidGarbageCollection 的容積已經(jīng)超過 numberOfHardLinks// 則將最老的緩存項(xiàng)從 hardLinksToAvoidGarbageCollection 中清除,FIFOif (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {hardLinksToAvoidGarbageCollection.removeLast();}}}}return result;}@Overridepublic Object removeObject(Object key) {// 清除指定的緩存項(xiàng)之前,也會(huì)先清理被 GC 的緩存項(xiàng)removeGarbageCollectedItems();return delegate.removeObject(key);}@Overridepublic void clear() {synchronized (hardLinksToAvoidGarbageCollection) {// 清理強(qiáng)引用集合hardLinksToAvoidGarbageCollection.clear();}// 清理被 GC 的緩存項(xiàng)removeGarbageCollectedItems();// 清理最底層的緩存項(xiàng)delegate.clear();}@Overridepublic String getId() {return delegate.getId();}@Overridepublic int getSize() {removeGarbageCollectedItems();return delegate.getSize();}public void setSize(int size) {this.numberOfHardLinks = size;} }WeakCache 的實(shí)現(xiàn)與 SoftCache 基本類似,唯一的區(qū)別在于其中使用 WeakEntry(繼承了 WeakReference)封裝真正的 value 對(duì)象,其他實(shí)現(xiàn)完全一樣。
另外,還有 ScheduledCache、LoggingCache、SynchronizedCache、SerializedCache 等。ScheduledCache 是周期性清理緩存的裝飾器,它的 clearInterval 字段 記錄了兩次緩存清理之間的時(shí)間間隔,默認(rèn)是一小時(shí),lastClear 字段 記錄了最近一次清理的時(shí)間戳。ScheduledCache 的 getObject()、putObject()、removeObject() 等核心方法,在執(zhí)行時(shí)都會(huì)根據(jù)這兩個(gè)字段檢測(cè)是否需要進(jìn)行清理操作,清理操作會(huì)清空緩存中所有緩存項(xiàng)。
LoggingCache 在 Cache 的基礎(chǔ)上提供了日志功能,它通過 hit 字段 和 request 字段 記錄了 Cache 的命中次數(shù)和訪問次數(shù)。在 LoggingCache.getObject()方法 中,會(huì)統(tǒng)計(jì)命中次數(shù)和訪問次數(shù) 這兩個(gè)指標(biāo),井按照指定的日志輸出方式輸出命中率。
SynchronizedCache 通過在每個(gè)方法上添加 synchronized 關(guān)鍵字,為 Cache 添加了同步功能,有點(diǎn)類似于 JDK 中 Collections 的 SynchronizedCollection 內(nèi)部類。
SerializedCache 提供了將 value 對(duì)象 序列化的功能。SerializedCache 在添加緩存項(xiàng)時(shí),會(huì)將 value 對(duì)應(yīng)的 Java 對(duì)象 進(jìn)行序列化,井將序列化后的 byte[]數(shù)組 作為 value 存入緩存 。SerializedCache 在獲取緩存項(xiàng)時(shí),會(huì)將緩存項(xiàng)中的 byte[]數(shù)組 反序列化成 Java 對(duì)象。不使用 SerializedCache 裝飾器 進(jìn)行裝飾的話,每次從緩存中獲取同一 key 對(duì)應(yīng)的對(duì)象時(shí),得到的都是同一對(duì)象,任意一個(gè)線程修改該對(duì)象都會(huì)影響到其他線程,以及緩存中的對(duì)象。而使用 SerializedCache 每次從緩存中獲取數(shù)據(jù)時(shí),都會(huì)通過反序列化得到一個(gè)全新的對(duì)象。SerializedCache 使用的序列化方式是 Java 原生序列化。
2、CacheKey
在 Cache 中唯一確定一個(gè)緩存項(xiàng),需要使用緩存項(xiàng)的 key 進(jìn)行比較,MyBatis 中因?yàn)樯婕?動(dòng)態(tài) SQL 等多方面因素, 其緩存項(xiàng)的 key 不能僅僅通過一個(gè) String 表示,所以 MyBatis 提供了 CacheKey 類 來表示緩存項(xiàng)的 key,在一個(gè) CacheKey 對(duì)象 中可以封裝多個(gè)影響緩存項(xiàng)的因素。CacheKey 中可以添加多個(gè)對(duì)象,由這些對(duì)象共同確定兩個(gè) CacheKey 對(duì)象 是否相同。
public class CacheKey implements Cloneable, Serializable {private static final long serialVersionUID = 1146682552656046210L;public static final CacheKey NULL_CACHE_KEY = new NullCacheKey();private static final int DEFAULT_MULTIPLYER = 37;private static final int DEFAULT_HASHCODE = 17;// 參與計(jì)算hashcode,默認(rèn)值DEFAULT_MULTIPLYER = 37private final int multiplier;// 當(dāng)前CacheKey對(duì)象的hashcode,默認(rèn)值DEFAULT_HASHCODE = 17private int hashcode;// 校驗(yàn)和private long checksum;private int count;// 由該集合中的所有元素 共同決定兩個(gè)CacheKey對(duì)象是否相同,一般會(huì)使用一下四個(gè)元素// MappedStatement的id、查詢結(jié)果集的范圍參數(shù)(RowBounds的offset和limit)// SQL語(yǔ)句(其中可能包含占位符"?")、SQL語(yǔ)句中占位符的實(shí)際參數(shù)private List<Object> updateList;// 構(gòu)造方法初始化屬性public CacheKey() {this.hashcode = DEFAULT_HASHCODE;this.multiplier = DEFAULT_MULTIPLYER;this.count = 0;this.updateList = new ArrayList<>();}public CacheKey(Object[] objects) {this();updateAll(objects);}public void update(Object object) {int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);// 重新計(jì)算count、checksum和hashcode的值count++;checksum += baseHashCode;baseHashCode *= count;hashcode = multiplier * hashcode + baseHashCode;// 將object添加到updateList集合updateList.add(object);}public int getUpdateCount() {return updateList.size();}public void updateAll(Object[] objects) {for (Object o : objects) {update(o);}}/*** CacheKey重寫了 equals() 和 hashCode()方法,這兩個(gè)方法使用上面介紹* 的 count、checksum、hashcode、updateList 比較兩個(gè) CacheKey對(duì)象 是否相同*/@Overridepublic boolean equals(Object object) {// 如果為同一對(duì)象,直接返回 trueif (this == object) {return true;}// 如果 object 都不是 CacheKey類型,直接返回 falseif (!(object instanceof CacheKey)) {return false;}// 類型轉(zhuǎn)換一下final CacheKey cacheKey = (CacheKey) object;// 依次比較 hashcode、checksum、count,如果不等,直接返回 falseif (hashcode != cacheKey.hashcode) {return false;}if (checksum != cacheKey.checksum) {return false;}if (count != cacheKey.count) {return false;}// 比較 updateList 中的元素是否相同,不同直接返回 falsefor (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;}@Overridepublic int hashCode() {return hashcode;}@Overridepublic 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();}@Overridepublic CacheKey clone() throws CloneNotSupportedException {CacheKey clonedCacheKey = (CacheKey) super.clone();clonedCacheKey.updateList = new ArrayList<>(updateList);return clonedCacheKey;} }總結(jié)
以上是生活随笔為你收集整理的apache缓存清理_深挖 Mybatis 源码:缓存模块的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 江苏有保障的计算机应用技术,2022年江
- 下一篇: im即时通讯源码/uniapp即时通讯i