LruCache,顾名思义,是一种具有 LRU 策略的缓存实现类。除此之外,MyBatis 还提

供了具有 FIFO 策略的缓存 FifoCache。不过并未提供 LFU 缓存

源代码:

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
public class LruCache implements Cache {
private final Cache delegate;
private Map<Object, Object> keyMap;
private Object eldestKey;

public LruCache(Cache delegate) {
this.delegate = delegate;
setSize(1024);
}

public int getSize() {
return delegate.getSize();
}

public void setSize(final int size) {
// 初始化 keyMap,注意,keyMap 的类型继承自 LinkedHashMap,
// 并覆盖了 removeEldestEntry 方法
keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
// 覆盖 LinkedHashMap 的 removeEldestEntry 方法
@Override
protected boolean removeEldestEntry(
Map.Entry<Object, Object> eldest) {
boolean tooBig = size() > size;
if (tooBig) {
// 获取将要被移除缓存项的键值
eldestKey = eldest.getKey();
}
return tooBig;
}
};
}

@Override
public void putObject(Object key, Object value) {
// 存储缓存项
delegate.putObject(key, value);
cycleKeyList(key);
}

@Override
public Object getObject(Object key) {
// 刷新 key 在 keyMap 中的位置
keyMap.get(key);
// 从被装饰类中获取相应缓存项
return delegate.getObject(key);
}

@Override
public Object removeObject(Object key) {
// 从被装饰类中移除相应的缓存项
return delegate.removeObject(key);
}

@Override
public void clear() {
delegate.clear();
keyMap.clear();
}

private void cycleKeyList(Object key) {
// 存储 key 到 keyMap 中
keyMap.put(key, key);
if (eldestKey != null) {
// 从被装饰类中移除相应的缓存项
delegate.removeObject(eldestKey);
eldestKey = null;
}
}
}

LruCache 的 keyMap 属性是实现LRU策略的关键,该属性类型继承自LinkedHashMap,并覆盖了removeEldestEntry方法

LinkedHashMap 可保持键值对的插入顺序,当插入一个新的键值对时,LinkedHashMap 内部的 tail 节点会指向最新插入的节点。head节点则指向第一个被插入的键值对,也就是最久未被访问的那个键值对。

默认情况下,LinkedHashMap 仅维护键值对的插入顺序。若要基于 LinkedHashMap 实现 LRU 缓存,还需通过构造方法将 LinkedHashMap 的 accessOrder 属性设为 true,此时 LinkedHashMap 会维护键值对的访问顺序。

上面代码中 getObject 方法中执行了这样一句代码keyMap.get(key) ,目的是刷新key 对应的键值对在 LinkedHashMap的位置。LinkedHashMap会将key对应的键值对移动到链表的尾部,尾部节点表示最久刚被访问过或者插入的节点。

除了需将 accessOrder 设为 true,还需覆盖removeEldestEntry 方法。LinkedHashMap 在插入新的键值对时会调用该方法,以决定是否在插入新的键值对后,移除老的键值对

当被装饰类的容量超出了keyMap的所规定的容量(由构造方法传入)后,keyMap会移除最长时间未被访问的键,并将该键保存到eldestKey中,然后由cycleKeyList方法将eldestKey传给被装饰类的removeObject方法,移除相应的缓存项目。