Mybatis 快取系統原始碼解析
本文從以下幾個方面介紹:
相關文章
前言
快取的相關介面
一級快取的實現過程
二級快取的實現過程
如何保證快取的執行緒安全
快取的裝飾器
相關文章
Mybatis Mapper.xml 配置檔案中 resultMap 節點的原始碼解析
Mybatis Mapper 介面原始碼解析(binding包)
Mybatis 資料來源和資料庫連線池原始碼解析(DataSource)
前言
在使用諸如 Mybatis 這種 ORM 框架的時候,一般都會提供快取功能,用來快取從資料庫查詢到的結果,當下一次查詢條件相同的時候,只需從快取中進行查詢返回即可,如果快取中沒有,再去查庫;一方面是提高查詢速度,另一方面是減少資料庫壓力;Mybatis 也提供了快取,它分為一級快取和二級快取,接下來就來看看它的快取系統是如何實現的。
快取系統的實現使用了 模板方法模式 和 裝飾器模式
接下來先來看下和快取相關的介面
Cache
Mybatis 使用 Cache 來表示快取,它是一個介面,定義了快取需要的一些方法,如下所示:
public interface Cache { //獲取快取的id,即 namespace String getId(); // 新增快取 void putObject(Object key, Object value); //根據key來獲取快取對應的值 Object getObject(Object key); // 刪除key對應的快取 Object removeObject(Object key); // 清空快取 void clear(); // 獲取快取中資料的大小 int getSize(); //取得讀寫鎖, 從3.2.6開始沒用了 ReadWriteLock getReadWriteLock(); }
對於每一個 namespace 都會建立一個快取的例項,Cache 實現類的構造方法都必須傳入一個 String 型別的ID,Mybatis自身的實現類都使用 namespace 作為 ID
PerpetualCache
Mybatis 為 Cache 介面提供的唯一一個實現類就是 PerpetualCache,這個唯一併不是說 Cache 只有一個實現類,只是快取的處理邏輯,Cache 還有其他的實現類,但是隻是作為裝飾器存在,只是對 Cache 進行包裝而已。
PerpetualCache 的實現比較簡單,就是把對應的 key-value 快取資料存入到 map 中,如下所示:
public class PerpetualCache implements Cache {
// id,一般對應mapper.xml 的namespace 的值
private String id;
// 用來存放資料,即快取底層就是使用 map 來實現的
private Map<Object, Object> cache = new HashMap<Object, Object>();
public PerpetualCache(String id) {
this.id = id;
}
//......其他的getter方法.....
// 新增快取
@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();
}
}
從上面的程式碼邏輯可以看到,mybatis 提供的快取底層就是使用一個 HashMap 來實現的,但是我們知道,HashMap 不是執行緒安全的,它是如何來保證快取中的執行緒安全問題呢?在後面講到 Cache 的包裝類就知道,它提供了一個 SynchronizedCache 的裝飾器類,就是用來包裝執行緒安全的,在該類中所有方法都加上了 synchronized 關鍵字。
CacheKey
Mybatis 的快取使用了 key-value 的形式存入到 HashMap 中,而 key 的話,Mybatis 使用了 CacheKey 來表示 key,它的生成規則為:mappedStementId + offset + limit + SQL + queryParams + environment生成一個雜湊碼.
public class CacheKey implements Cloneable, Serializable {
private static final int DEFAULT_MULTIPLYER = 37;
private static final int DEFAULT_HASHCODE = 17;
// 參與計算hashcode,預設值為37
private int multiplier;
// CacheKey 物件的 hashcode ,預設值 17
private int hashcode;
// 檢驗和
private long checksum;
// updateList 集合的個數
private int count;
// 由該集合中的所有物件來共同決定兩個 CacheKey 是否相等
private List<Object> updateList;
public int getUpdateCount() {
return updateList.size();
}
// 呼叫該方法,向 updateList 集合新增對應的物件
public void update(Object object) {
if (object != null && object.getClass().isArray()) {
// 如果是陣列,則迴圈處理每一項
int length = Array.getLength(object);
for (int i = 0; i < length; i++) {
Object element = Array.get(object, i);
doUpdate(element);
}
} else {
doUpdate(object);
}
}
// 計算 count checksum hashcode 和把物件新增到 updateList 集合中
private void doUpdate(Object object) {
int baseHashCode = object == null ? 1 : object.hashCode();
count++;
checksum += baseHashCode;
baseHashCode *= count;
hashcode = multiplier * hashcode + baseHashCode;
updateList.add(object);
}
// 判斷兩個 CacheKey 是否相等
@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;
}
// 如果前幾項都不滿足,則迴圈遍歷 updateList 集合,判斷每一項是否相等,如果有一項不相等則這兩個CacheKey不相等
for (int i = 0; i < updateList.size(); i++) {
Object thisObject = updateList.get(i);
Object thatObject = cacheKey.updateList.get(i);
if (thisObject == null) {
if (thatObject != null) {
return false;
}
} else {
if (!thisObject.equals(thatObject)) {
return false;
}
}
}
return true;
}
@Override
public int hashCode() {
return hashcode;
}
}
如果需要進行快取,則如何建立 CacheKey 呢?下面這個就是建立 一個 CacheKey 的方法:
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
//cacheKey 物件
CacheKey cacheKey = new CacheKey();
// 向 updateList 存入id
cacheKey.update(ms.getId());
// 存入offset
cacheKey.update(rowBounds.getOffset());
// 存入limit
cacheKey.update(rowBounds.getLimit());
// 存入sql
cacheKey.update(boundSql.getSql());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
String propertyName = parameterMapping.getProperty();
MetaObject metaObject = configuration.newMetaObject(parameterObject);
Object value = metaObject.getValue(propertyName);
// 存入每一個引數
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// 存入 environmentId
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
從上面 CacheKey 和建立 CacheKey 的程式碼邏輯可以看出,Mybatis 的快取使用了 mappedStementId + offset + limit + SQL + queryParams + environment 生成的hashcode作為 key。
瞭解了上述和快取相關的介面後,接下來就來看看 Mybatis 的快取系統是如何實現的,Mybatis 的快取分為一級快取和二級快取,一級快取是在 BaseExecutor 中實現的,二級快取是在 CachingExecutor 中實現的。
Executor
Executor 介面定義了操作資料庫的基本方法,SqlSession 的相關方法就是基於 Executor 介面實現的,它定義了操作資料庫的方法如下:
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
// insert | update | delete 的操作方法
int update(MappedStatement ms, Object parameter) throws SQLException;
// 查詢,帶分頁,帶快取
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
// 查詢,帶分頁
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
// 查詢儲存過程
<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
//重新整理批處理語句
List<BatchResult> flushStatements() throws SQLException;
// 事務提交
void commit(boolean required) throws SQLException;
// 事務回滾
void rollback(boolean required) throws SQLException;
// 建立快取的key
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
// 是否快取
boolean isCached(MappedStatement ms, CacheKey key);
// 清空快取
void clearLocalCache();
// 延遲載入
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
// 獲取事務
Transaction getTransaction();
}
一級快取
BaseExecutor
BaseExecutor 是一個抽象類,實現了 Executor 介面,並提供了大部分方法的實現,只有 4 個基本方法:doUpdate, doQuery, doQueryCursor, doFlushStatement 沒有實現,還是一個抽象方法,由子類實現,這 4 個方法相當於模板方法中變化的那部分。
Mybatis 的一級快取就是在該類中實現的。
Mybatis 的一級快取是會話級別的快取,Mybatis 每建立一個 SqlSession 物件,就表示開啟一次資料庫會話,在一次會話中,應用程式很可能在短時間內反覆執行相同的查詢語句,如果不對資料進行快取,則每查詢一次就要執行一次資料庫查詢,這就造成資料庫資源的浪費。又因為通過 SqlSession 執行的操作,實際上由 Executor 來完成資料庫操作的,所以在 Executor 中會建立一個簡單的快取,即一級快取;將每次的查詢結果快取起來,再次執行查詢的時候,會先查詢一級快取,如果命中,則直接返回,否則再去查詢資料庫並放入快取中。
一級快取的生命週期與 SqlSession 的生命週期相同,當呼叫 Executor.close 方法的時候,快取變得不可用。一級快取是預設開啟的,一般情況下不需要特殊的配置,如果需要特殊配置,則可以通過外掛的形式來實現
public abstract class BaseExecutor implements Executor {
// 事務,提交,回滾,關閉事務
protected Transaction transaction;
// 底層的 Executor 物件
protected Executor wrapper;
// 延遲載入佇列
protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
// 一級快取,用於快取查詢結果
protected PerpetualCache localCache;
// 一級快取,用於快取輸出型別引數(儲存過程)
protected PerpetualCache localOutputParameterCache;
protected Configuration configuration;
// 用來記錄巢狀查詢的層數
protected int queryStack;
private boolean closed;
protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.transaction = transaction;
this.deferredLoads = new ConcurrentLinkedQueue<DeferredLoad>();
this.localCache = new PerpetualCache("LocalCache");
this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
this.closed = false;
this.configuration = configuration;
this.wrapper = this;
}
// 4 個抽象方法,由子類實現,模板方法中可變部分
protected abstract int doUpdate(MappedStatement ms, Object parameter)throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)throws SQLException;
protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)throws SQLException;
// 執行 insert | update | delete 語句,呼叫 doUpdate 方法實現,在執行這些語句的時候,會清空快取
public int update(MappedStatement ms, Object parameter) throws SQLException {
// ....
// 清空快取
clearLocalCache();
// 執行SQL語句
return doUpdate(ms, parameter);
}
// 重新整理批處理語句,且執行快取中還沒執行的SQL語句
@Override
public List<BatchResult> flushStatements() throws SQLException {
return flushStatements(false);
}
public List<BatchResult> flushStatements(boolean isRollBack) throws SQLException {
// ...
// doFlushStatements 的 isRollBack 引數表示是否執行快取中的SQL語句,false表示執行,true表示不執行
return doFlushStatements(isRollBack);
}
// 查詢儲存過程
@Override
public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
return doQueryCursor(ms, parameter, rowBounds, boundSql);
}
// 事務的提交和回滾
@Override
public void commit(boolean required) throws SQLException {
// 清空快取
clearLocalCache();
// 重新整理批處理語句,且執行快取中的QL語句
flushStatements();
if (required) {
transaction.commit();
}
}
@Override
public void rollback(boolean required) throws SQLException {
if (!closed) {
try {
// 清空快取
clearLocalCache();
// 重新整理批處理語句,且不執行快取中的SQL
flushStatements(true);
} finally {
if (required) {
transaction.rollback();
}
}
}
}
在上面的程式碼邏輯中,執行update型別的語句會清空快取,且執行結果不需要進行快取,而在執行查詢語句的時候,需要對資料進行快取,如下所示:
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 獲取查詢SQL
BoundSql boundSql = ms.getBoundSql(parameter);
// 建立快取的key,建立邏輯在 CacheKey中已經分析過了
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
// 執行查詢
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
// 執行查詢邏輯
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
// ....
if (queryStack == 0 && ms.isFlushCacheRequired()) {
// 如果不是巢狀查詢,且 <select> 的 flushCache=true 時才會清空快取
clearLocalCache();
}
List<E> list;
try {
// 巢狀查詢層數加1
queryStack++;
// 首先從一級快取中進行查詢
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
// 如果命中快取,則處理儲存過程
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 如果快取中沒有對應的資料,則查資料庫中查詢資料
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
// ... 處理延遲載入的相關邏輯
return list;
}
// 從資料庫查詢資料
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
// 在快取中新增佔位符
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
// 查庫操作,由子類實現
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
// 刪除佔位符
localCache.removeObject(key);
}
// 將從資料庫查詢的結果新增到一級快取中
localCache.putObject(key, list);
// 處理儲存過程
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
二級快取
Mybatis 提供的二級快取是應用級別的快取,它的生命週期和應用程式的生命週期相同,且與二級快取相關的配置有以下 3 個:
1. mybatis-config.xml 配置檔案中的 cacheEnabled 配置,它是二級快取的總開關,只有該配置為 true ,後面的快取配置才會生效。預設為 true,即二級快取預設是開啟的。
2. Mapper.xml 配置檔案中配置的 <cache> 和 <cache-ref>標籤,如果 Mapper.xml 配置檔案中配置了這兩個標籤中的任何一個,則表示開啟了二級快取的功能,在 Mybatis 解析 SQL 原始碼分析一 文章中已經分析過,如果配置了 <cache> 標籤,則在解析配置檔案的時候,會為該配置檔案指定的 namespace 建立相應的 Cache 物件作為其二級快取(預設為 PerpetualCache 物件),如果配置了 <cache-ref> 節點,則通過 ref 屬性的namespace值引用別的Cache物件作為其二級快取。通過 <cache> 和 <cache-ref> 標籤來管理其在namespace中二級快取功能的開啟和關閉
3. <select> 節點中的 useCache 屬性也可以開啟二級快取,該屬性表示查詢的結果是否要存入到二級快取中,該屬性預設為 true,也就是說 <select> 標籤預設會把查詢結果放入到二級快取中。
Mybatis 的二級快取是用 CachingExecutor 來實現的,它是 Executor 的一個裝飾器類。為 Executor 物件添加了快取的功能。
在介紹 CachingExecutor 之前,先來看看 CachingExecutor 依賴的兩個類,TransactionalCacheManager 和 TransactionalCache。
TransactionalCache
TransactionalCache 實現了 Cache 介面,主要用於儲存在某個 SqlSession 的某個事務中需要向某個二級快取中新增的資料,程式碼如下:
public class TransactionalCache implements Cache {
// 底層封裝的二級快取對應的Cache物件
private Cache delegate;
// 為true時,表示當前的 TransactionalCache 不可查詢,且提交事務時會清空快取
private boolean clearOnCommit;
// 存放需要新增到二級快取中的資料
private Map<Object, Object> entriesToAddOnCommit;
// 存放為命中快取的 CacheKey 物件
private Set<Object> entriesMissedInCache;
public TransactionalCache(Cache delegate) {
this.delegate = delegate;
this.clearOnCommit = false;
this.entriesToAddOnCommit = new HashMap<Object, Object>();
this.entriesMissedInCache = new HashSet<Object>();
}
// 新增快取資料的時候,先暫時放到 entriesToAddOnCommit 集合中,在事務提交的時候,再把資料放入到二級快取中,避免髒資料
@Override
public void putObject(Object key, Object object) {
entriesToAddOnCommit.put(key, object);
}
// 提交事務,
public void commit() {
if (clearOnCommit) {
delegate.clear();
}
// 把 entriesToAddOnCommit 集合中的資料放入到二級快取中
flushPendingEntries();
reset();
}
// 把 entriesToAddOnCommit 集合中的資料放入到二級快取中
private void flushPendingEntries() {
for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
// 放入到二級快取中
delegate.putObject(entry.getKey(), entry.getValue());
}
for (Object entry : entriesMissedInCache) {
if (!entriesToAddOnCommit.containsKey(entry)) {
delegate.putObject(entry, null);
}
}
}
// 事務回滾
public void rollback() {
// 把未命中快取的資料清除掉
unlockMissedEntries();
reset();
}
private void unlockMissedEntries() {
for (Object entry : entriesMissedInCache) {
delegate.removeObject(entry);
}
}
TransactionalCacheManager
TransactionalCacheManager 用於管理 CachingExecutor 使用的二級快取:
public class TransactionalCacheManager {
//用來管理 CachingExecutor 使用的二級快取
// key 為對應的CachingExecutor 使用的二級快取
// value 為對應的 TransactionalCache 物件
private Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>();
public void clear(Cache cache) {
getTransactionalCache(cache).clear();
}
public Object getObject(Cache cache, CacheKey key) {
return getTransactionalCache(cache).getObject(key);
}
public void putObject(Cache cache, CacheKey key, Object value) {
getTransactionalCache(cache).putObject(key, value);
}
public void commit() {
for (TransactionalCache txCache : transactionalCaches.values()) {
txCache.commit();
}
}
public void rollback() {
for (TransactionalCache txCache : transactionalCaches.values()) {
txCache.rollback();
}
}
// 所有的呼叫都會呼叫 TransactionalCache 的方法來實現
private TransactionalCache getTransactionalCache(Cache cache) {
TransactionalCache txCache = transactionalCaches.get(cache);
if (txCache == null) {
txCache = new TransactionalCache(cache);
transactionalCaches.put(cache, txCache);
}
return txCache;
}
}
CachingExecutor
接下來看下 二級快取的實現 CachingExecutor :
public class CachingExecutor implements Executor {
// 底層的 Executor
private Executor delegate;
private TransactionalCacheManager tcm = new TransactionalCacheManager();
// 查詢方法
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 獲取 SQL
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 建立快取key,在CacheKey中已經分析過建立過程
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
// 查詢
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
// 獲取查詢語句所在namespace對應的二級快取
Cache cache = ms.getCache();
// 是否開啟了二級快取
if (cache != null) {
// 根據 <select> 的屬性 useCache 的配置,決定是否需要清空二級快取
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
// 二級快取不能儲存輸出引數,否則拋異常
ensureNoOutParams(ms, parameterObject, boundSql);
// 從二級快取中查詢對應的值
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
// 如果二級快取沒有命中,則呼叫底層的 Executor 查詢,其中會先查詢一級快取,一級快取也未命中,才會去查詢資料庫
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
// 查詢到的資料放入到二級快取中去
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
// 如果沒有開啟二級快取,則直接呼叫底層的 Executor 查詢,還是會先查一級快取
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
以上就是 Mybatis 的二級快取的主要實現過程,CachingExecutor , TransactionalCacheManager 和 TransactionalCache 的關係如下所示,主要是通過 TransactionalCache 來操作二級快取的。
此外,CachingExecutor 還有其他的一些方法,主要是呼叫底層封裝的 Executor 來實現的。
以上就是 Mybatis 的一級快取和二級快取的實現過程。
Cache 裝飾器
在介紹 Cache 介面的時候,說到,Cache 介面由很多的裝飾器類,共 10 個,添加了不同的功能,如下所示:
來看看 SynchronizedCache 裝飾器類吧,在上面的快取實現中介紹到了 Mybatis 其實就是使用 HashMap 來實現快取的,即把資料放入到 HashMap中,但是 HashMap 不是線安全的,Mybatis 是如何來保證快取中的執行緒安全問題呢?就是使用了 SynchronizedCache 來保證的,它是一個裝飾器類,其中的方法都加上了 synchronized 關鍵字:
public class SynchronizedCache implements Cache {
private Cache delegate;
public SynchronizedCache(Cache delegate) {
this.delegate = delegate;
}
@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);
}
// ............
}
接下來看下新增 Cache 裝飾器的方法,在 CacheBuilder.build() 方法中進行新增:
public class CacheBuilder {
//...........
// 建立快取
public Cache build() {
// 設定快取的實現類
setDefaultImplementations();
Cache cache = newBaseCacheInstance(implementation, id);
setCacheProperties(cache);
// 新增裝飾器類
if (PerpetualCache.class.equals(cache.getClass())) {
for (Class<? extends Cache> decorator : decorators) {
cache = newCacheDecoratorInstance(decorator, cache);
setCacheProperties(cache);
}
// 為 Cache 新增裝飾器
cache = setStandardDecorators(cache);
} else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
cache = new LoggingCache(cache);
}
return cache;
}
// 設定 Cache 的預設實現類為 PerpetualCache
private void setDefaultImplementations() {
if (implementation == null) {
implementation = PerpetualCache.class;
if (decorators.isEmpty()) {
decorators.add(LruCache.class);
}
}
}
// 新增裝飾器
private Cache setStandardDecorators(Cache cache) {
try {
// 新增 ScheduledCache 裝飾器
if (clearInterval != null) {
cache = new ScheduledCache(cache);
((ScheduledCache) cache).setClearInterval(clearInterval);
}
// 新增SerializedCache裝飾器
if (readWrite) {
cache = new SerializedCache(cache);
}
// 新增 LoggingCache 裝飾器
cache = new LoggingCache(cache);
// 新增 SynchronizedCache 裝飾器,保證執行緒安全
cache = new SynchronizedCache(cache);
if (blocking) {
// 新增 BlockingCache 裝飾器
cache = new BlockingCache(cache);
}
return cache;
}
}
還有其他的裝飾器,這裡就不一一列出來了。
到這裡 Mybatis 的快取系統模組就分析完畢了。