Mybatis3.5.1源碼分析
- Mybatis-SqlSessionFactoryBuilder,XMLConfigBuilder,XPathParser源碼解析
- Mybatis-Configuration源碼解析
- Mybatis-事務(wù)對象源碼解析
- Mybatis-數(shù)據(jù)源源碼解析
- Mybatis緩存策略源碼解析
- Mybatis-DatabaseIdProvider源碼解析
- Mybatis-TypeHandler源碼解析
- Mybatis-Reflector源碼解析
- Mybatis-ObjectFactory,ObjectWrapperFactory源碼分析
- Mybatis-Mapper各類標(biāo)簽封裝類源碼解析
- Mybatis-XMLMapperBuilder,XMLStatmentBuilder源碼分析
- Mybatis-MapperAnnotationBuilder源碼分析
- [Mybatis-MetaObject,MetaClass源碼解析]http://www.itdecent.cn/p/f51fa552f30a)
- Mybatis-LanguageDriver源碼解析
- Mybatis-SqlSource源碼解析
- Mybatis-SqlNode源碼解析
- Mybatis-KeyGenerator源碼解析
- Mybatis-Executor源碼解析
- Mybatis-ParameterHandler源碼解析
- Mybatis-StatementHandler源碼解析
- Mybatis-DefaultResultSetHandler(一)源碼解析
- Mybatis-DefaultResultSetHandler(二)源碼解析
- Mybatis-ResultHandler,Cursor,RowBounds 源碼分析
- Mybatis-MapperProxy源碼解析
- Mybatis-SqlSession源碼解析
- Mybatis-Interceptor源碼解析
Cache
緩存策略類都是基于裝飾者設(shè)計模式進(jìn)行開發(fā)的
/**
* 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:串行外設(shè)接口。)
* <p>
* One instance of cache will be created for each namespace. 每個命名空間都會創(chuàng)建一個緩存的實(shí)例對象
* <p>
* The cache implementation must have a constructor that receives the cache id as an String parameter.
* 繼承{@link Cache}的實(shí)現(xiàn)類必須有一個可接收一個緩存ID的String類型參數(shù)的構(gòu)造函數(shù)
* <p>
* MyBatis will pass the namespace as id to the constructor.
* Mybatis會將命名空間作為id傳入構(gòu)造方法
*
* <pre>
* public MyCache(final String id) {
* if (id == null) {
* throw new IllegalArgumentException("Cache instances require an ID");
* }
* this.id = id;
* initialize();
* }
* </pre>
* @author Clinton Begin
*/
public interface Cache {
/**
* 獲取緩存對象的唯一標(biāo)識,一般是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. 查詢結(jié)果
*/
void putObject(Object key, Object value);
/**
* 從緩存對象中獲取key對應(yīng)的value
* @param key The key
* @return The object stored in the cache.
*/
Object getObject(Object key);
/**
* <p>
* 移除key對應(yīng)的value
* </p>
* 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后,這個方法只是在回滾在緩存中丟失了的任何之前的值。
* 這使得任何一個阻塞緩存釋放可能之前寫入鍵的鎖。
* 一個阻塞緩存當(dāng)值為null時加一個鎖,在值再次后臺時釋放這個所。
* 這樣其他線程會等待可用的價值,而不是數(shù)據(jù)庫。
* @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.
* 獲取緩存對象中存儲的鍵/值對的數(shù)量
* @return The number of elements stored in the cache (not its capacity).
*/
int getSize();
/**
* 獲取讀寫鎖,這個方法mybatis的所有Cache實(shí)現(xiàn)類都沒有重寫過,都是直接返回null
* <p>
* 從3.2.6起這個方法不再被框架核心調(diào)用,任何需要的鎖,都必須由緩存供應(yīng)商提供
* </p>
* Optional. As of 3.2.6 this method is no longer called by the core.
* <p>
* 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接口實(shí)現(xiàn)類,里面就是維護(hù)著一個HashMap
* <p>
* Cache接口只有這唯一一個基礎(chǔ)實(shí)現(xiàn),其他實(shí)現(xiàn)類全都是裝飾模式持有另一個緩存對象
* </p>
* @author Clinton Begin
*/
public class PerpetualCache implements Cache {
/**
* 緩存對象的唯一標(biāo)識
*/
private final String id;
/**
* 對象內(nèi)部維護(hù)的HashMap,緩存Map
*/
private Map<Object, Object> 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去實(shí)現(xiàn)相等,如果當(dāng)前類型對象的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,如果當(dāng)前類型對象的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.
* 使用先進(jìn)先出緩存策略的緩存裝飾類
* @author Clinton Begin
*/
public class FifoCache implements Cache {
/**
* 委托Cache對象
*/
private final Cache delegate;
/**
* 鍵列表
* <p>
* Deque是Queue的子接口,我們知道Queue是一種隊(duì)列形式,而Deque則是雙向隊(duì)列,它支持從兩個端點(diǎn)方向檢索和插入元素,
* 因此Deque既可以支持LIFO形式也可以支持LIFO形式.Deque接口是一種比Stack和Vector更為豐富的抽象數(shù)據(jù)形式,因?yàn)樗瑫r實(shí)現(xiàn)了以上兩者.
* </p>
* <p>
* 初始化時,該屬性的實(shí)現(xiàn)類默認(rèn)是{@link LinkedList}
* </p>
*/
private final Deque<Object> keyList;
/**
* 最大緩存數(shù),初始化時為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}的當(dāng)前緩存數(shù)
*/
@Override
public int getSize() {
return delegate.getSize();
}
/**
* 設(shè)置最大緩存數(shù)
* @param size
*/
public void setSize(int size) {
this.size = size;
}
/**
* 添加數(shù)據(jù)進(jìn)緩存
* @param key Can be any object but usually it is a {@link @CacheKey} 可以是任何對象,但一般是CacheKey對象
* @param value The result of a select. 查詢結(jié)果
*/
@Override
public void putObject(Object key, Object value) {
cycleKeyList(key);
delegate.putObject(key, value);
}
/**
* 從{@link #delegate}中獲取{@link @key}對應(yīng)的緩存數(shù)據(jù)
*/
@Override
public Object getObject(Object key) {
return delegate.getObject(key);
}
/**
* 從{@link #delegate}中刪除{@link @key}對應(yīng)的緩存數(shù)據(jù)
*/
@Override
public Object removeObject(Object key) {
return delegate.removeObject(key);
}
/**
* 清空緩存,{@link #delegate},{@link #keyList}都會清空
*/
@Override
public void clear() {
delegate.clear();
keyList.clear();
}
/**
* 獲取讀寫鎖,該類未實(shí)現(xiàn)讀寫鎖,直接返回null
* @return
*/
@Override
public ReadWriteLock getReadWriteLock() {
return null;
}
/**
* 周期檢查健名列表
* <p>
* 將{@link @key}記錄到{@link #keyList}中,再判斷{@link #keyList}大小是否超過設(shè)定的最大值,超過就取出{@link #keyList}的
* 第一個元素賦值給{@link @oldestKey},然后刪掉{@link #delegate}的{@link @oldestKey}緩存
* </p>
* @param key
*/
private void cycleKeyList(Object key) {
keyList.addLast(key);
//檢查當(dāng)前keyList的大小是否大于設(shè)定的最大值
if (keyList.size() > size) {
Object oldestKey = keyList.removeFirst();//移除列表中的第一個元素,并返回
delegate.removeObject(oldestKey);//移除對應(yīng)的緩存
}
}
}
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 {
/**
* 真正存放緩存數(shù)據(jù)的Cache對象,委托類
*/
private final Cache delegate;
/**
* 記錄著每個key的訪問次數(shù)
*/
private Map<Object, Object> keyMap;
/**
* 最近最少用的元素的key
*/
private Object eldestKey;
/**
* 設(shè)置委托的Cache對象,并初始化最大緩存數(shù),默認(rèn)是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}的緩存數(shù)
* @return
*/
@Override
public int getSize() {
return delegate.getSize();
}
/**
* 設(shè)置最大緩存數(shù)
* <p>
* 每次調(diào)用該方法,都會重新新建一個LinkeHashMap實(shí)例對賦值給{@link #keyMap},這意思之前的keyMap所記錄的每個元素的訪問次數(shù)
* 都會丟失,重新記錄訪問次數(shù)。而{@link #eldestKey}并不會置空,還是會在每次添加緩存數(shù)據(jù)后,刪除對應(yīng){@link #eldestKey}的
* {@link #delegate}里的元素。之所以這樣設(shè)計,推測可能是因?yàn)樵O(shè)計者任務(wù)重新記錄每個元素的訪問次數(shù)并不會造成太大的業(yè)務(wù)問題,并且
* 既然重新調(diào)用了該方法,之前的擴(kuò)容算法和初始化容量大小都應(yīng)該都應(yīng)該按照新的{@link @size}來重新設(shè)置,以保證性能的最佳
* </p>
* @param size
*/
public void setSize(final int size) {
/**
* LinkedHashMap構(gòu)造函數(shù):
* 第一個參數(shù)initialCapacity:初始化容量大小
* 第2個參數(shù)loadFactor:后面如果LinkedHashMap需要增大長度,按照capacity*loadFactor取整后增長
* 第3個參數(shù)accessOrder:
* accessOrder設(shè)置為false,表示不是訪問順序而是插入順序存儲的,這也是默認(rèn)值,表示LinkedHashMap中存儲的順序是按照調(diào)用put方法插入的順序進(jìn)行排序的
*/
keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
private static final long serialVersionUID = 4267176411845948333L;
/**
* <p>
* 該方法是LinkedHashMap提供的一個鉤子方法,是交給用戶自己根據(jù)業(yè)務(wù)實(shí)現(xiàn)的。該方法會在添加
* 完元素后被調(diào)用,如果返回的是false,就不會刪除最近最少使用的元素。默認(rèn)是返回false。
* </p>
* <p>
* 如果大于{@link LruCache#keyMap}的大小大于設(shè)定的{@link #size},
* 就會返回true,并將最近最少使用的元素的key賦值給{@link LruCache#eldestKey}
* </p>
* @param eldest
* @return
*/
@Override
protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
boolean tooBig = size() > size;
if (tooBig) {
eldestKey = eldest.getKey();//記錄最近最少使用的元素
}
return tooBig;
}
};
}
/**
* 添加緩存數(shù)據(jù)
* <p>
* 除了添加緩存數(shù)據(jù)到{@link #delegate}以外,還將{@link @key}加入到{@link #keyMap}中進(jìn)行記錄,
* 并將將{@link #delegate}里最近最少用的元素刪除。
* </p>
* @param key Can be any object but usually it is a {@link @CacheKey} 可以是任何對象,但一般是CacheKey對象
* @param value The result of a select. 查詢結(jié)果
*/
@Override
public void putObject(Object key, Object value) {
delegate.putObject(key, value);
// 將{@link @key}加入到{@link #keyMap}中進(jìn)行記錄,并將將{@link #delegate}里最近最少用的元素刪除。
cycleKeyList(key);
}
/**
* 獲取緩存數(shù)據(jù)
* <p>
* 除了從{@link #delegate}中取出對應(yīng){@link @key}的緩存數(shù)據(jù),{@link @keyMap}也會對{@link @key}記錄訪問次數(shù)。
* </p>
*/
@Override
public Object getObject(Object key) {
keyMap.get(key); //touch 這里會使得key在keyMap里的訪問次數(shù)加1
return delegate.getObject(key);
}
/**
* 刪除{@link #delegate}的對應(yīng)的{@link @key}的緩存數(shù)據(jù)
*/
@Override
public Object removeObject(Object key) {
return delegate.removeObject(key);
}
/**
* 清空{(diào)@link #delegate}和{@link #keyMap}的所有數(shù)據(jù)
*/
@Override
public void clear() {
delegate.clear();
keyMap.clear();
}
/**
* 獲取讀寫鎖,該類未實(shí)現(xiàn)讀寫鎖,直接返回null
*/
@Override
public ReadWriteLock getReadWriteLock() {
return null;
}
/**
* 周期檢查健名列表
* <p>
* 將{@link @key}加入到{@link #keyMap}中進(jìn)行記錄,并將將{@link #delegate}里最近最少用的元素刪除。
* </p>
* @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.
* 軟引用回收策略 緩存裝飾類
* <p>
* 軟引用只有當(dāng)內(nèi)存不足時才會被垃圾收集器回收。這里的實(shí)現(xiàn)機(jī)制中,使用了一個鏈表來保證一定數(shù)量的值即使內(nèi)存不足也不會被回收,
* 但是沒有保存在該鏈表的值則有可能會被回收
* </p>
* @author Clinton Begin
*/
public class SoftCache implements Cache {
/**
* 硬列表,以避免GC,初始化對象實(shí)例為LinkedList
* <p>用于保存一定數(shù)量強(qiáng)引用的值</p>
*/
private final Deque<Object> hardLinksToAvoidGarbageCollection;
/**
* 垃圾收集條目的隊(duì)列
* <p>引用隊(duì)列,當(dāng)被垃圾收集器回收時,會將軟引用對象放入此隊(duì)列</p>
* <p>
* 當(dāng)GC回收時,會將對象
* </p>
*/
private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
/**
* 真正的緩存類,委托Cache對象
*/
private final Cache delegate;
/**
* 保存強(qiáng)引用值的數(shù)量,初始化為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}的當(dāng)前緩存數(shù)
*/
@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));
}
/**
* 獲取緩存數(shù)據(jù),獲取的數(shù)據(jù)如果不為null,會將這個數(shù)據(jù)放入強(qiáng)引用隊(duì)列中,隊(duì)列會判斷當(dāng)前可容納的數(shù)量,超過了就采用先進(jìn)先出的策略進(jìn)行移除。
* @param key The key
* @return 有可能返回null。因?yàn)镚C可能已經(jīng)清理相關(guān)數(shù)據(jù)
*/
@Override
public Object getObject(Object key) {
Object result = null;
@SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cache
SoftReference<Object> softReference = (SoftReference<Object>) delegate.getObject(key);
if (softReference != null) {
result = softReference.get();
if (result == null) {
// 該值被垃圾收集器回收,移除掉該項(xiàng)
delegate.removeObject(key);
} else {
/**
* 這里以及下面的clear,想不通為什么要加hardLinksToAvoidGarbageCollection的同步?(在WeakCache中卻沒有加同步)
* --我的推測:因?yàn)镚C是一條線程,同步能使得主線程先執(zhí)行下面代碼后,GC才會工作,這樣才能防止GC在主線程執(zhí)行下面代碼時
* 回收了對象。
*/
// See #586 (and #335) modifications need more than a read lock
synchronized (hardLinksToAvoidGarbageCollection) {
//存入經(jīng)常訪問的鍵值到鏈表(最多256元素),防止垃圾回收
hardLinksToAvoidGarbageCollection.addFirst(result);
if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {
// 超出容量,則移除最先保存的引用
//因?yàn)榧尤胧羌尤氲搅斜淼牡谝粋€位置,隨意最后一個就是最先加入的元素。
hardLinksToAvoidGarbageCollection.removeLast();
}
}
}
}
return result;
}
/**
* 刪除對應(yīng){@link @key}的緩存數(shù)據(jù)
* @param key The key
* @return
*/
@Override
public Object removeObject(Object key) {
// 移除被垃圾收集器回收的鍵值
removeGarbageCollectedItems();
return delegate.removeObject(key);
}
/**
* 清空緩存數(shù)據(jù)
* <p>
* 清空強(qiáng)引用,移除被垃圾收集器回收的鍵值,清空緩存數(shù)據(jù)
* </p>
*/
@Override
public void clear() {
//清空強(qiáng)引用
synchronized (hardLinksToAvoidGarbageCollection) {
hardLinksToAvoidGarbageCollection.clear();
}
// 移除被垃圾收集器回收的鍵值
removeGarbageCollectedItems();
//清空緩存數(shù)據(jù)
delegate.clear();
}
@Override
public ReadWriteLock getReadWriteLock() {
return null;
}
/**
* 刪除被垃圾收集器回收的鍵值
*/
private void removeGarbageCollectedItems() {
SoftEntry sv;
// 清空被垃圾收集器回收的value其相關(guān)聯(lián)的鍵以及軟引用
while ((sv = (SoftEntry) queueOfGarbageCollectedEntries.poll()) != null) {
delegate.removeObject(sv.key);
}
}
/**
* 繼承了{(lán)@link SoftReference},使得傳進(jìn)來的value轉(zhuǎn)變成軟引用。
*<p>
* 這里將其Value作為軟引用,而不是用key,因?yàn)镵ey不能被回收,如果被移除的話,就會影響到整個體系,
* 最底層的實(shí)現(xiàn)使用HashMap實(shí)現(xiàn)的,沒有Key,就沒有辦法移除相關(guān)的值。反過來,值被回收了,將軟引用對象放到隊(duì)列中,
* 可以根據(jù)Key調(diào)用removeObject移除該關(guān)聯(lián)的鍵和軟引用對象。
*</p>
*/
private static class SoftEntry extends SoftReference<Object> {
/**
* 保存與value相關(guān)聯(lián)的Key,因?yàn)橐坏┍焕占骰厥眨瑒t此軟引用對象會被放到關(guān)聯(lián)的引用隊(duì)列中,
* 這樣就可以根據(jù)Key,移除該鍵值對
*/
private final Object key;
SoftEntry(Object key, Object value, ReferenceQueue<Object> 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.
* <p>
* 弱引用回收策略 緩存裝飾類,邏輯和{@link SoftCache}非常類似。就直接看{@link SoftCache}邏輯算了,我懶得寫了
* </p>
* <p>
* 弱引用的對象一旦被垃圾收集器發(fā)現(xiàn),則會被回收,無論內(nèi)存是否足夠
* </p>
* @author Clinton Begin
*/
public class WeakCache implements Cache {
/**
*
*/
private final Deque<Object> hardLinksToAvoidGarbageCollection;
private final ReferenceQueue<Object> 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<Object> weakReference = (WeakReference<Object>) 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<Object> {
private final Object key;
private WeakEntry(Object key, Object value, ReferenceQueue<Object> 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模式,則會輸出命中率日志。
* <p>
* 參考博客:<a >https://www.cnblogs.com/jabnih/p/5705640.html</a>
* </p>
* @author Clinton Begin
*/
public class LoggingCache implements Cache {
/**
* log的名字一般是命名空間,從{@link #delegate}的{@link Cache#getId()}方法獲取。
*/
private final Log log;
private final Cache delegate;
/**
* 請求次數(shù)
*/
protected int requests = 0;
/**
* 命中次數(shù)
*/
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);
}
/**
* 獲取緩存數(shù)據(jù),并統(tǒng)計請求次數(shù)和命中次數(shù)
* @param key The key
* @return
*/
@Override
public Object getObject(Object key) {
requests++;//每次獲取,請求次數(shù)+1
final Object value = delegate.getObject(key);
if (value != null) {
hits++;//獲取的緩存數(shù)據(jù)時存在的,命中次數(shù)+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);
}
/**
* 獲取命中率
* <p>
* 命中率 = 命中次數(shù) / 請求次數(shù)
* </p>
* @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,但是并沒有開始一個定時任務(wù),而是在使用Cache的時候,才去檢查時間是否到了。
* <p>
* 參考博客:<a >https://www.cnblogs.com/jabnih/p/5705640.html</a>
* </p>
* @author Clinton Begin
*/
public class ScheduledCache implements Cache {
private final Cache delegate;
/**
* 清除緩存數(shù)據(jù)的時間間隔,初始化為1小時
*/
protected long clearInterval;
/**
* 上一次清除的時間,初始化時賦值當(dāng)前時間
*/
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);
}
/**
* 清空所有緩存數(shù)據(jù)
*/
@Override
public void clear() {
lastClear = System.currentTimeMillis();//記錄當(dāng)前時間
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);
}
/**
* 判斷是否到了清空數(shù)據(jù)的時間
*/
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;
/**
* 序列化功能,將值序列化后存到緩存中。該功能用于緩存返回一份實(shí)例的Copy,用于保存線程安全。
* <p>
* 該緩存只是保存在內(nèi)存里
* </p>
* <p>
* 參考博客:<a >https://www.cnblogs.com/jabnih/p/5705640.html</a>
* </p>
* @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 {
// 此方法只有在待序列化的類第一次序列化的時候才會被調(diào)用
// 遍歷所支持的ClassLoader,加載對應(yīng)的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,實(shí)現(xiàn)比較簡單,直接使用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級緩存事務(wù)緩沖區(qū)
* <p>
* 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.
* 這個類保留著所有在對話期間被添加到二級緩存的緩存數(shù)據(jù)。緩存數(shù)據(jù)在事務(wù)提交后會被傳入二級緩存中,或者在事務(wù)會話回退后被丟棄。
* 阻塞著已經(jīng)添加緩存的緩存數(shù)據(jù),因此任何get()方法返回出來的緩存丟失,會隨后將由put(),所以任何的和對應(yīng)的key關(guān)聯(lián)的鎖可以被釋放掉。
* <p>
* 事務(wù)緩存,在提交的時候,才真正的放到Cache中,或者回滾的時候清除掉,對Cache沒有影響
* </p>
* @author Clinton Begin
* @author Eduardo Macarron
*/
public class TransactionalCache implements Cache {
private static final Log log = LogFactory.getLog(TransactionalCache.class);
/**
* 真正存放緩存數(shù)據(jù)的Cache對象,委托類
*/
private final Cache delegate;
/**
* 是否在commit時清除二級緩存的標(biāo)記
*/
private boolean clearOnCommit;
/**
* 需要在commit時提交到二級緩存的數(shù)據(jù)
*/
private final Map<Object, Object> entriesToAddOnCommit;
/**
* 緩存未命中的數(shù)據(jù),事務(wù)commit時,也會放入二級緩存(key,null)
*/
private final Set<Object> 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();
}
/**
* 獲取緩存數(shù)據(jù)
* <p>
* 首先會在對應(yīng)的二級緩存對象中查詢,并將未命中的key記錄到entiresMissedInCache中,之后根據(jù)clearOnCommit決定返回值
* </p>
* @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;
}
/**
* 將沒有提及的數(shù)據(jù)記錄下來
* @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,并且設(shè)置clearOnCommit為true
*/
@Override
public void clear() {
clearOnCommit = true;
entriesToAddOnCommit.clear();
}
public void commit() {
if (clearOnCommit) {
// 清空二級緩存
delegate.clear();
}
//將數(shù)據(jù)刷新到二級緩存
flushPendingEntries();
//重置clearOnCommit為false,清空entriesToAddOnCommit、entriesMissedInCache集合
reset();
}
/**
* 回退
* <p>
* 根據(jù){@link #entriesMissedInCache},刪除二級緩存的數(shù)據(jù),重置clearOnCommit為false,
* 清空entriesToAddOnCommit、entriesMissedInCache集合
* </p>
*/
public void rollback() {
//解鎖丟失的緩存數(shù)據(jù),使其根據(jù){@link #entriesMissedInCache}刪除二級緩存的數(shù)據(jù)
unlockMissedEntries();
//重置clearOnCommit為false,清空entriesToAddOnCommit、entriesMissedInCache集合
reset();
}
/**
* 重置一級緩存
* <p>
* {@link #entriesToAddOnCommit}和{@link #entriesMissedInCache}全清空,而不清空二級緩存。
* 并將{@link #clearOnCommit}標(biāo)記設(shè)置為false
* </p>
*
*/
private void reset() {
clearOnCommit = false;
entriesToAddOnCommit.clear();
entriesMissedInCache.clear();
}
/**
* 將數(shù)據(jù)刷新到二級緩存
* <p>
* 將entriesToAddOnCommit和entriesMissedInCache添加到二級緩存,entriesMissedInCache只存儲了key,所以添加到二級緩存時,value為null
* </p>
*/
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)) {//entriesMissedInCache只存儲了key,所以添加到二級緩存時,value為null
delegate.putObject(entry, null);
}
}
}
/**
* 解鎖丟失的緩存數(shù)據(jù)
* <p>
* 刪除在二級緩存中關(guān)于{@link #entriesMissedInCache}的所有元素的緩存數(shù)據(jù),這里捕捉了所有移除緩存時的異常,保證每個緩存都能刪除
* </p>
*/
private void unlockMissedEntries() {
for (Object entry : entriesMissedInCache) {
//將try..catch放在循環(huán)里面,能夠使得每個緩存數(shù)據(jù)都會執(zhí)行刪除,而不會導(dǎo)致有一個刪除數(shù)據(jù)因?yàn)閽伋霎惓?,而?dǎo)致其他緩存數(shù)據(jù)沒有刪除
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);
}
}
}
}