源码分析-Mybatis源码阅读-缓存

源码解析-Mybatis
01 源码分析-Mybatis源码阅读准备
02 源码分析-Mybatis源码阅读-会话层
03 源码分析-Mybatis源码阅读-执行器
04 源码分析-Mybatis源码阅读-Statement语句处理器
05 源码分析-Mybatis源码阅读-参数处理器
06 源码分析-Mybatis源码阅读-结果集处理器
07 源码分析-Mybatis源码阅读-主键生成器
08 源码分析-Mybatis源码阅读-懒加载机制
09 源码分析-Mybatis源码阅读-游标
10 源码分析-Mybatis源码阅读-类型处理器
11 源码分析-Mybatis源码阅读-缓存
12 源码分析-Mybatis源码阅读-Mapper代理

一 缓存类说明

缓存类的相关代码,主要在包org.apache.ibatis.cache中,以下是该包下所有类的说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.
└── org
└── apache
└── ibatis
└── cache
   ├── Cache.java 缓存提供者的接口
   ├── CacheException.java 缓存异常类
   ├── CacheKey.java 缓存key包装类
   ├── NullCacheKey.java 空缓存可以-已废弃
   ├── TransactionalCacheManager.java 事务缓存管理器
   ├── decorators
   │   ├── BlockingCache.java 阻塞缓存装饰类
   │   ├── FifoCache.java 使用先进先出缓存策略的缓存装饰类
   │   ├── LoggingCache.java 日志功能装饰类 输出命中率日志
   │   ├── LruCache.java 最近最少使用的缓存策略的缓存装饰类
   │   ├── ScheduledCache.java 定时清空Cache装饰类
   │   ├── SerializedCache.java 序列化缓存装饰类
   │   ├── SoftCache.java 软引用缓存装饰类
   │   ├── SynchronizedCache.java 同步Cache装饰类
   │   ├── TransactionalCache.java 事务缓存装饰类
   │   └── WeakCache.java 弱引用缓存装饰类
   └── impl
      └── PerpetualCache.java 永久缓存 Cache接口实现类,里面就是维护着一个HashMap

其中PerpetualCacheCache的唯一基础实现,其他实现类全部都是装饰模式持有另一个缓存对象。以下是这些缓存类的UML图:

image-20210610065223738

二 缓存类源码解读

Cache接口类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/**
* 为缓存提供者的接口(SPI:串行外设接口。)
* SPI for cache providers.
* <p>
* 每个命名空间都会创建一个缓存的实例对象
* One instance of cache will be created for each namespace.
* <p>
* The cache implementation must have a constructor that receives the cache id as an String parameter.
* 缓存实现必须有一个构造函数,它接收缓存 ID 作为字符串参数
* <p>
* MyBatis will pass the namespace as id to the constructor.
* MyBatis 会将命名空间作为 id 传递给构造函数
*
* <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 {

/**
* 获取缓存对象的唯一标识,一般是Mapper.xml的命名空间
* @return The identifier of this cache
*/
String getId();

/**
* 保存key/value到缓存对象中
* @param key 可以是任何对象,但一般是CacheKey对象
* Can be any object but usually it is a {@link CacheKey}
* @param value 查询结果
* The result of a select.
*/
void putObject(Object key, Object value);

/**
* 从缓存对象中获取key对应的value
* @param key
* The key
* @return The object stored in the cache.
*/
Object getObject(Object key);

/**
* 移除key对应的value
*
* 从 3.3.0 开始,仅在回滚缓存中丢失的任何先前值期间调用此方法。
* 这允许任何阻塞缓存释放先前可能已放在密钥上的锁。
* 阻塞缓存在值为空时放置一个锁,并在该值再次返回时释放它。
* 这样其他线程将等待该值可用而不是访问数据库。
*
* 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
*/
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).
*/
int getSize();

/**
* 获取读写锁,这个方法mybatis的所有Cache实现类都没有重写过,都是直接返回null
* Optional. As of 3.2.6 this method is no longer called by the core.
* 可选的。从 3.2.6 开始,框架核心不再调用此方法。
* <p>
* Any locking needed by the cache must be provided internally by the cache provider.
*
* @return A ReadWriteLock
*/
default ReadWriteLock getReadWriteLock() {
return null;
}

}

PerpetualCache基础实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/**
* 永久缓存 Cache接口实现类,里面就是维护着一个HashMap
* <p>
* Cache接口只有这唯一一个基础实现,其他实现类全都是装饰模式持有另一个缓存对象
* </p>
* @author Clinton Begin
*/
public class PerpetualCache implements Cache {

/** 缓存对象的唯一标识 */
private final String id;

/** 对象内部维护的HashMap,缓存Map */
private final 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();
}

/**
* 通过判断ID去实现相等,如果当前类型对象的ID属性为null会抛出{@link CacheException}
* @param o
* @return
*/
@Override
public boolean equals(Object o) {
if (getId() == null) {
throw new CacheException("Cache instances require an ID.");
}
// 判断地址
if (this == o) {
return true;
}
// 非Cache的子类
if (!(o instanceof Cache)) {
return false;
}

Cache otherCache = (Cache) o;
// 判断ID
return getId().equals(otherCache.getId());
}

/**
* 把属性ID的hashCode作为本类对象的hashCode,如果当前类型对象的ID属性为null会抛出{@link CacheException}
* @return
*/
@Override
public int hashCode() {
if (getId() == null) {
throw new CacheException("Cache instances require an ID.");
}
return getId().hashCode();
}

}

FifoCache装饰类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/**
* 使用先进先出缓存策略的缓存装饰类
* FIFO (first in, first out) cache decorator.
*
* @author Clinton Begin
*/
public class FifoCache implements Cache {

/** 委托Cache对象 */
private final Cache delegate;
/**
* 键列表
* <p>
* Deque是Queue的子接口,我们知道Queue是一种队列形式,而Deque则是双向队列,它支持从两个端点方向检索和插入元素,
* 因此Deque既可以支持FIFO形式也可以支持LIFO形式.Deque接口是一种比Stack和Vector更为丰富的抽象数据形式,因为它同时实现了以上两者.
* </p>
* <p>
* 初始化时,该属性的实现类默认是{@link LinkedList}
* </p>
*/
private final Deque<Object> keyList;
/** 最大缓存数,初始化时为1024 */
private int size;

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

/**
* 取{@link #delegate}的ID
* @return
*/
@Override
public String getId() {
return delegate.getId();
}

/**
* 取{@link #delegate}的当前缓存数
* @return
*/
@Override
public int getSize() {
return delegate.getSize();
}

/**
* 设置最大缓存数
* @param size
*/
public void setSize(int size) {
this.size = size;
}

/**
* 添加数据进缓存
* @param key 可以是任何对象,但一般是CacheKey对象
* @param value 查询结果
*/
@Override
public void putObject(Object key, Object value) {
cycleKeyList(key);
delegate.putObject(key, value);
}

/**
* 从{@link #delegate}中获取{@link @key}对应的缓存数据
* @param key
* @return
*/
@Override
public Object getObject(Object key) {
return delegate.getObject(key);
}

/**
* 从{@link #delegate}中删除{@link @key}对应的缓存数据
* @param key
* @return
*/
@Override
public Object removeObject(Object key) {
return delegate.removeObject(key);
}

/**
* 清空缓存,{@link #delegate},{@link #keyList}都会清空
*/
@Override
public void clear() {
delegate.clear();
keyList.clear();
}

/**
* 周期检查健名列表
* <p>
* 将{@link @key}记录到{@link #keyList}中,再判断{@link #keyList}大小是否超过设定的最大值,超过就取出{@link #keyList}的
* 第一个元素赋值给{@link @oldestKey},然后删掉{@link #delegate}的{@link @oldestKey}缓存
* </p>
* @param key
*/
private void cycleKeyList(Object key) {
keyList.addLast(key);
if (keyList.size() > size) {
Object oldestKey = keyList.removeFirst();
delegate.removeObject(oldestKey);
}
}

}

LruCache装饰类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/**
* 最近最少使用的缓存策略的缓存装饰类
* Lru (least recently used) cache decorator.
*
* @author Clinton Begin
*/
public class LruCache implements Cache {

/** 真正存放缓存数据的Cache对象,委托类 */
private final Cache delegate;
/** 记录着每个key的访问次数 */
private Map<Object, Object> keyMap;
/** 最近最少用的元素的key */
private Object eldestKey;

/**
* 设置委托的Cache对象,并初始化最大缓存数,默认是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}的缓存数
* @return
*/
@Override
public int getSize() {
return delegate.getSize();
}

/**
* 设置最大缓存数
* <p>
* 每次调用该方法,都会重新新建一个LinedHashMap实例对赋值给{@link #keyMap},这意味着之前的keyMap所记录的每个元素的访问次数
* 都会丢失,重新记录访问次数。而{@link #eldestKey}并不会置空,还是会在每次添加缓存数据后,删除对应{@link #eldestKey}的
* {@link #delegate}里的元素。之所以这样设计,推测可能是因为设计者任务重新记录每个元素的访问次数并不会造成太大的业务问题,并且
* 既然重新调用了该方法,之前的扩容算法和初始化容量大小都应该都应该按照新的{@link @size}来重新设置,以保证性能的最佳
* </p>
* @param size
*/
public void setSize(final int size) {
/*
* LinkedHashMap构造函数:
* 第一个参数initialCapacity:初始化容量大小
* 第2个参数loadFactor:后面如果LinkedHashMap需要增大长度,按照capacity*loadFactor取整后增长
* 第3个参数accessOrder:
* accessOrder设置为false,表示不是访问顺序而是插入顺序存储的,这也是默认值,表示LinkedHashMap中存储的顺序是按照调用put方法插入的顺序进行排序的
*/
keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
private static final long serialVersionUID = 4267176411845948333L;

/*
* <p>
* 该方法是LinkedHashMap提供的一个钩子方法,是交给用户自己根据业务实现的。该方法会在添加
* 完元素后被调用,如果返回的是false,就不会删除最近最少使用的元素。默认是返回false。
* </p>
* <p>
* 如果大于{@link LruCache#keyMap}的大小大于设定的{@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;
}
};
}

/**
* 添加缓存数据
* <p>
* 除了添加缓存数据到{@link #delegate}以外,还将{@link @key}加入到{@link #keyMap}中进行记录,
* 并将将{@link #delegate}里最近最少用的元素删除。
* </p>
* @param key Can be any object but usually it is a {@link @CacheKey} 可以是任何对象,但一般是CacheKey对象
* @param value The result of a select. 查询结果
*/
@Override
public void putObject(Object key, Object value) {
delegate.putObject(key, value);
cycleKeyList(key);
}

/**
* 获取缓存数据
* <p>
* 除了从{@link #delegate}中取出对应{@link @key}的缓存数据,{@link @keyMap}也会对{@link @key}记录访问次数。
* </p>
*/
@Override
public Object getObject(Object key) {
keyMap.get(key); // touch 这里会使得key在keyMap里的访问次数加1
return delegate.getObject(key);
}

/**
* 删除{@link #delegate}的对应的{@link @key}的缓存数据
*/
@Override
public Object removeObject(Object key) {
return delegate.removeObject(key);
}

/**
* 清空{@link #delegate}和{@link #keyMap}的所有数据
*/
@Override
public void clear() {
delegate.clear();
keyMap.clear();
}

/**
* 周期检查健名列表
* <p>
* 将{@link @key}加入到{@link #keyMap}中进行记录,并将{@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装饰类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/**
* 软引用回收策略 缓存装饰类
* <p>
* 软引用只有当内存不足时才会被垃圾收集器回收。这里的实现机制中,使用了一个链表来保证一定数量的值即使内存不足也不会被回收,
* 但是没有保存在该链表的值则有可能会被回收
* </p>
* Soft Reference cache decorator
* Thanks to Dr. Heinz Kabutz for his guidance here.
*
* @author Clinton Begin
*/
public class SoftCache implements Cache {
/** 硬列表,以避免GC,初始化对象实例为LinkedList 用于保存一定数量强引用的值 */
private final Deque<Object> hardLinksToAvoidGarbageCollection;
/**
* 垃圾收集条目的队列
* <p>引用队列,当被垃圾收集器回收时,会将软引用对象放入此队列</p>
* <p>当GC回收时,会将对象</p>
*/
private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
/** 真正的缓存类,委托Cache对象 */
private final Cache delegate;
/** 保存强引用值的数量,初始化为256 */
private int numberOfHardLinks;

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();
}

/**
* 获取取{@link #delegate}的当前缓存数
* @return
*/
@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));
}

/**
* 获取缓存数据,获取的数据如果不为null,会将这个数据放入强引用队列中,队列会判断当前可容纳的数量,超过了就采用先进先出的策略进行移除。
* @param key The key
* @return 有可能返回null。因为GC可能已经清理相关数据
*/
@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) {
// 该值被垃圾收集器回收,移除掉该项
delegate.removeObject(key);
} else {
// 因为hardLinksToAvoidGarbageCollection非线程安全
// 多线程情况下如果不加锁 hardLinksToAvoidGarbageCollection的size可能会超过设定的值numberOfHardLinks
// See #586 (and #335) modifications need more than a read lock
synchronized (hardLinksToAvoidGarbageCollection) {
// 存入经常访问的键值到链表(最多256元素),防止垃圾回收
hardLinksToAvoidGarbageCollection.addFirst(result);
if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {
// 超出容量,则移除最先保存的引用
// 因为加入是加入到列表的第一个位置,随意最后一个就是最先加入的元素。
hardLinksToAvoidGarbageCollection.removeLast();
}
}
}
}
return result;
}

/**
* 删除对应{@link @key}的缓存数据
* @param key The key
* @return
*/
@Override
public Object removeObject(Object key) {
// 移除被垃圾收集器回收的键值
removeGarbageCollectedItems();
return delegate.removeObject(key);
}

/**
* 清空缓存数据
* <p>
* 清空强引用,移除被垃圾收集器回收的键值,清空缓存数据
* </p>
*/
@Override
public void clear() {
// 清空强引用
synchronized (hardLinksToAvoidGarbageCollection) {
hardLinksToAvoidGarbageCollection.clear();
}
// 移除被垃圾收集器回收的键值
removeGarbageCollectedItems();
// 清空缓存数据
delegate.clear();
}

/**
* 删除被垃圾收集器回收的键值
*/
private void removeGarbageCollectedItems() {
SoftEntry sv;
// 清空被垃圾收集器回收的value其相关联的键以及软引用
while ((sv = (SoftEntry) queueOfGarbageCollectedEntries.poll()) != null) {
delegate.removeObject(sv.key);
}
}

/**
* 继承了{@link SoftReference},使得传进来的value转变成软引用。
* <p>
* 这里将其Value作为软引用,而不是用key,因为Key不能被回收,如果被移除的话,就会影响到整个体系,
* 最底层的实现使用HashMap实现的,没有Key,就没有办法移除相关的值。反过来,值被回收了,将软引用对象放到队列中,
* 可以根据Key调用removeObject移除该关联的键和软引用对象。
* </p>
*/
private static class SoftEntry extends SoftReference<Object> {
/**
* 保存与value相关联的Key,因为一旦被垃圾收集器回收,则此软引用对象会被放到关联的引用队列中,
* 这样就可以根据Key,移除该键值对
*/
private final Object key;

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

}

WeakCache装饰类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/**
* 弱引用回收策略 缓存装饰类,逻辑和{@link SoftCache}几乎一样。直接参考{@link SoftCache}逻辑
* 弱引用的对象一旦被垃圾收集器发现,则会被回收,无论内存是否足够
*
* Weak Reference cache decorator.
* Thanks to Dr. Heinz Kabutz for his guidance here.
*
* @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 {
synchronized (hardLinksToAvoidGarbageCollection) {
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() {
synchronized (hardLinksToAvoidGarbageCollection) {
hardLinksToAvoidGarbageCollection.clear();
}
removeGarbageCollectedItems();
delegate.clear();
}

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;
}
}

}

ScheduledCache装饰类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/**
* 定时清空Cache,但是并没有开始一个定时任务,而是在使用Cache的时候,才去检查时间是否到了。
* @author Clinton Begin
*/
public class ScheduledCache implements Cache {

private final Cache delegate;
/** 清除缓存数据的时间间隔,初始化为1小时 */
protected long clearInterval;
/** 上一次清除的时间,初始化时赋值当前时间 */
protected long lastClear;

public ScheduledCache(Cache delegate) {
this.delegate = delegate;
this.clearInterval = TimeUnit.HOURS.toMillis(1);
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);
}

/**
* 判断是否到了清空数据的时间
*/
private boolean clearWhenStale() {
if (System.currentTimeMillis() - lastClear > clearInterval) {
clear();
return true;
}
return false;
}

}

SerializedCache装饰类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/**
* 序列化功能,将值序列化后存到缓存中。该功能用于缓存返回一份实例的Copy,用于保存线程安全。
* <p>
* 该缓存只是保存在内存里
* </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 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) {
SerialFilterChecker.check();
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 {
// 此方法只有在待序列化的类第一次序列化的时候才会被调用
// 遍历所支持的ClassLoader,加载对应的Class
return Resources.classForName(desc.getName());
}

}

}

SynchronizedCache装饰类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* 同步Cache,实现比较简单,直接使用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);
}

}

TransactionalCache 装饰类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/**
* 事务缓存装饰类 2级缓存事务缓冲区
* 此类保存在会话期间要添加到二级缓存的所有缓存条目。
* 如果 Session 回滚,则在调用 commit 或丢弃时将条目发送到缓存。添加了阻塞缓存支持。
* 因此,任何返回缓存未命中的 get() 都将跟随一个 put(),因此可以释放与该键关联的任何锁。
* The 2nd level cache transactional buffer.
* <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.
*
* @author Clinton Begin
* @author Eduardo Macarron
*/
public class TransactionalCache implements Cache {

private static final Log log = LogFactory.getLog(TransactionalCache.class);

/** 真正存放缓存数据的Cache对象,委托类 */
private final Cache delegate;
/** 是否在commit时清除二级缓存的标记 */
private boolean clearOnCommit;
/** 需要在commit时提交到二级缓存的数据 */
private final Map<Object, Object> entriesToAddOnCommit;
/** 缓存未命中的数据,事务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();
}

/**
* 获取缓存数据
* <p>
* 首先会在对应的二级缓存对象中查询,并将未命中的key记录到entriesMissedInCache中,之后根据clearOnCommit决定返回值
* </p>
* @param key The key
* @return
*/
@Override
public Object getObject(Object key) {
// issue #116
Object object = delegate.getObject(key);
if (object == null) {
// 记录未命中的key
entriesMissedInCache.add(key);
}
// issue #146
if (clearOnCommit) {
return null;
} else {
return object;
}
}

/**
* 将没有提交的数据记录下来
* @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,并且设置clearOnCommit为true
*/
@Override
public void clear() {
clearOnCommit = true;
entriesToAddOnCommit.clear();
}

public void commit() {
if (clearOnCommit) {
// 清空二级缓存
delegate.clear();
}
// 将数据刷新到二级缓存
flushPendingEntries();
// 重置clearOnCommit为false,清空entriesToAddOnCommit、entriesMissedInCache集合
reset();
}

/**
* 回退
* <p>
* 根据{@link #entriesMissedInCache},删除二级缓存的数据,重置clearOnCommit为false,
* 清空entriesToAddOnCommit、entriesMissedInCache集合
* </p>
*/

public void rollback() {
// 解锁丢失的缓存数据,使其根据{@link #entriesMissedInCache}删除二级缓存的数据
unlockMissedEntries();
// 重置clearOnCommit为false,清空entriesToAddOnCommit、entriesMissedInCache集合
reset();
}

/**
* 重置一级缓存
* <p>
* {@link #entriesToAddOnCommit}和{@link #entriesMissedInCache}全清空,而不清空二级缓存。
* 并将{@link #clearOnCommit}标记设置为false
* </p>
*/
private void reset() {
clearOnCommit = false;
entriesToAddOnCommit.clear();
entriesMissedInCache.clear();
}

/**
* 将数据刷新到二级缓存
* <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)) {
delegate.putObject(entry, null);
}
}
}

/**
* 解锁丢失的缓存数据
* <p>
* 删除在二级缓存中关于{@link #entriesMissedInCache}的所有元素的缓存数据,这里捕捉了所有移除缓存时的异常,保证每个缓存都能删除
* </p>
*/
private void unlockMissedEntries() {
for (Object entry : entriesMissedInCache) {
try {
delegate.removeObject(entry);
} catch (Exception e) {
log.warn("Unexpected exception while notifying a rollback to the cache adapter. "
+ "Consider upgrading your cache adapter to the latest version. Cause: " + e);
}
}
}

}

LoggingCache装饰类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/**
* 日志功能,装饰类,用于记录缓存的命中率,如果开启了DEBUG模式,则会输出命中率日志。
* @author Clinton Begin
*/
public class LoggingCache implements Cache {

/** log的名字一般是命名空间,从{@link #delegate}的{@link Cache#getId()}方法获取。 */
private final Log log;
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);
}

/**
* 获取缓存数据,并统计请求次数和命中次数
* @param key The key
* @return
*/
@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);
}

/**
* 获取命中率
* <p>
* 命中率 = 命中次数 / 请求次数
* </p>
* @return
*/
private double getHitRatio() {
return (double) hits / (double) requests;
}

}

BlockingCache装饰类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/**
* 阻塞缓存装饰类
*
* EhCache 的 BlockingCache 装饰器的简单而低效的版本。
* 当在缓存中找不到元素时,它会锁定缓存键。
* 这样,其他线程将等到该元素被填充而不是访问数据库。
*
* 就其性质而言,如果使用不当,此实现可能会导致死锁。
*
* <p>Simple blocking decorator
*
* <p>Simple and inefficient version of EhCache's BlockingCache decorator.
* It sets a lock over a cache key when the element is not found in cache.
* This way, other threads will wait until this element is filled instead of hitting the database.
*
* <p>By its nature, this implementation can cause deadlock when used incorrecly.
*
* @author Eduardo Macarron
*
*/
public class BlockingCache implements Cache {

private long timeout;
/** 委托Cache对象 */
private final Cache delegate;
/** 每个key的锁 value为count=1的CountDownLatch */
private final ConcurrentHashMap<Object, CountDownLatch> 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) {
// 获取对象 先获取到key的锁
acquireLock(key);
Object value = delegate.getObject(key);
// 获取到了 则直接释放锁 否则不释放锁
if (value != null) {
releaseLock(key);
}
return value;
}

/**
* 并不会真的移除key的缓存 仅仅是释放锁而已
* @param key
* @return
*/
@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();
}

/**
* 获取锁
* @param key
*/
private void acquireLock(Object key) {
CountDownLatch newLatch = new CountDownLatch(1);
while (true) {
// locks中如果不存在key对应的锁则put一个锁
CountDownLatch latch = locks.putIfAbsent(key, newLatch);
if (latch == null) {
break;
}
try {
// 如果有等待超时时间
if (timeout > 0) {
// 等待超时时间结束
boolean acquired = latch.await(timeout, TimeUnit.MILLISECONDS);
// 如果超时时间结束未获取到锁则抛异常
if (!acquired) {
throw new CacheException(
"Couldn't get a lock in " + timeout + " for the key " + key + " at the cache " + delegate.getId());
}
} else {
// 不限时间等待锁 可能造成死锁
latch.await();
}
} catch (InterruptedException e) {
throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);
}
}
}

/**
* 释放锁
* @param key
*/
private void releaseLock(Object key) {
// 从locks中移除
CountDownLatch latch = locks.remove(key);
if (latch == null) {
throw new IllegalStateException("Detected an attempt at releasing unacquired lock. This should never happen.");
}
latch.countDown();
}

public long getTimeout() {
return timeout;
}

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