Cache模块
MyBatis中缓存模块相关代码位于cache包下,其中Cache接口是缓存模块中最核心的接口,它定义了所有缓存的基本行为。接口源码:
// Cache 接口 是缓存模块中最核心的接口,它定义了所有缓存的基本行为。
public interface Cache {
/**
* @return The identifier of this cache
*/
// 该缓存对象的id
String getId();
/**
* @param key Can be any object but usually it is a {@link CacheKey}
* @param value The result of a select.
*/
// 向缓存中添加数据,一般情况下, key是CacheKey,value是查询结果
void putObject(Object key, Object value);
/**
* @param key The key
* @return The object stored in the cache.
*/
// 根据指定的 key ,在缓存中查找对应的结果对象
Object getObject(Object key);
/**
* 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.
*
*
* @param key The key
* @return Not used
*/
// 删除 key 对应的缓存项
Object removeObject(Object key);
/**
* Clears this cache instance.
*/
// 清空缓存
void clear();
/**
* Optional. This method is not called by the core.
*
* @return The number of elements stored in the cache (not its capacity).
*/
// 缓存项的个数,该方法不会被 MyBatis 核心代码使用,所以可提供空实现
int getSize();
/**
* 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
*/
// 获取读写锁,该方法不会被 MyBatis 核心代码使用,所以可提供空实现
default ReadWriteLock getReadWriteLock() {
return null;
}
}
其余扩展行为均使用装饰器模式实现。以下列举出主要的类。源码如下:
/**
* Copyright ${license.git.copyrightYears} 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 org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;
/**
* @author Clinton Begin
*/
/**
* Cache接口的实现类有多个,但大部分都是装饰器,只有 PerpetualCache 提供了 Cache 接口的基本实现。<br/>
* PerpetualCache 在缓存模块中扮演着 ConcreteComponent 的角色,其实现比较简单,底层使<br/>
* HashMap 记录缓存项,也是通过该 HashMap 对象的方法实现的 Cache 接口中定义的相应方法。<br/>
*/
public class PerpetualCache implements Cache {
// Cache 对象的唯一标识
private final String id;
// 用于记录缓存项的 Map 对象
private Map<Object, Object> cache = new HashMap<>();
public PerpetualCache(String id) {
this.id = id;
}
@Override
public String getId() {
return id;
}
// 下面所有的方法都是通过 cache 字段记录这个 HashMap 对象的相应方法实现的
@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();
}
// ... 重写 equals ()方法和 hashCode ()方法,两者都只关心 id 字段,并不关心 cache 字段(略)
@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;
return getId().equals(otherCache.getId());
}
@Override
public int hashCode() {
if (getId() == null) {
throw new CacheException("Cache instances require an ID.");
}
return getId().hashCode();
}
}
/**
* Copyright ${license.git.copyrightYears} 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 org.apache.ibatis.cache.Cache;
/**
* Soft Reference cache decorator
* Thanks to Dr. Heinz Kabutz for his guidance here.
*
* @author Clinton Begin
*/
public class SoftCache implements Cache {
// SoftCache 中,最近使用的一部分缓存项不会被 GC 回收,这就是通过将其 value 添加到
// hardLinksToAvoidGarbageCollection 集合中实现的(即有强引用指向其 value)
// hardLinksToAvoidGarbageCollection 集合是 LinkedList<Object >类型
private final Deque<Object> hardLinksToAvoidGarbageCollection;
// ReferenceQueue 引用队列,用于记录已经被GC回收的缓存项所对应的SoftEntry对象
private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
private final Cache delegate;// 底层被装饰的底层 Cache 对象
private int numberOfHardLinks;// 强连接的个数, 认值是 256
public SoftCache(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();// 清除已经被 GC 回收的缓存项
// 向缓存中添加缓存项
delegate.putObject(key, new SoftEntry(key, value, queueOfGarbageCollectedEntries));
}
/**
* 除了从缓存 对应 value ,处理被 GC 回收的 value 对应的
* 缓存项 还会更新 hardLinksToAvoidGarbageCollection 集合
*/
@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();// 获取 SoftRef erence 引用的 value
if (result == null) {// 已经被GC回收
delegate.removeObject(key);// 从缓存 清除对应的缓存项
} else {// 未被GC回收
// See #586 (and #335) modifications need more than a read lock
synchronized (hardLinksToAvoidGarbageCollection) {
// 缓存项 value hardLinksToAvoidGarbageCollection 集合中保存
hardLinksToAvoidGarbageCollection.addFirst(result);
if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {
// 超过 nurnberOfHardLinks ,则将最老的缓存项从
// hardLinksToAvoidGarbageCollection 集合中清除,有点类似于先进先出队f1]
hardLinksToAvoidGarbageCollection.removeLast();
}
}
}
}
return result;
}
@Override
public Object removeObject(Object key) {
removeGarbageCollectedItems();
return delegate.removeObject(key);
}
@Override
public void clear() {
synchronized (hardLinksToAvoidGarbageCollection) {
hardLinksToAvoidGarbageCollection.clear();// 清理强引用集合
}
removeGarbageCollectedItems();// 清理被 GC 回收的缓存项
delegate.clear();// 清理底层 delegate 缓存中的缓存项
}
private void removeGarbageCollectedItems() {
SoftEntry sv;
// 造历 queueOfGarbageCollectedEntries 集合
while ((sv = (SoftEntry) queueOfGarbageCollectedEntries.poll()) != null) {
delegate.removeObject(sv.key);// 将已经被 GC 回收的 value 对象对 存项清除
}
}
/**
* SoftCache 中缓存项的 value SoftEn町对象, SoftEntry 继承 Soft:Reference 其中指向
* key 的引用是强引用, 而指向 value 的引用是软引用
*/
private static class SoftEntry extends SoftReference<Object> {
private final Object key;
SoftEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
// 指向value 的引用是软引用,且关联了引用队列
super(value, garbageCollectionQueue);
this.key = key;// 强引用
}
}
}