0%

mybatis之缓存

缓存

Cache接口

mybatis中包含一级缓存和二级缓存,在本质上是一样的,使用的都是Cache接口的实现。

Cache接口定义了所有缓存的基本行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface Cache {
// 缓存对象的id
String getId();
// 向缓存中添加数据,key一般为CacheKey
void putObject(Object var1, Object var2);
// 通过key来获取缓存值
Object getObject(Object var1);
// 通过key删除对应的缓存
Object removeObject(Object var1);
// 清除缓存
void clear();
// 获取缓存中数据的大小
int getSize();
// 3.2.6之后不在使用
ReadWriteLock getReadWriteLock();
}

对于每一个 namespace 都会创建一个缓存的实例,Cache 实现类的构造方法都必须传入一个 String 类型的ID,Mybatis自身的实现类都使用 namespace 作为 ID

下面介绍一下Cache接口的实现

PerpetualCache

使用HashMap进行存储

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
public class PerpetualCache implements Cache {
private final String id;
// 存储缓存数据
private final Map<Object, Object> cache = new HashMap();

public PerpetualCache(String id) {
this.id = id;
}

public String getId() {
return this.id;
}

public int getSize() {
return this.cache.size();
}

public void putObject(Object key, Object value) {
this.cache.put(key, value);
}

public Object getObject(Object key) {
return this.cache.get(key);
}

public Object removeObject(Object key) {
return this.cache.remove(key);
}

public void clear() {
this.cache.clear();
}

public boolean equals(Object o) {
if (this.getId() == null) {
throw new CacheException("Cache instances require an ID.");
} else if (this == o) {
return true;
} else if (!(o instanceof Cache)) {
return false;
} else {
Cache otherCache = (Cache)o;
return this.getId().equals(otherCache.getId());
}
}

public int hashCode() {
if (this.getId() == null) {
throw new CacheException("Cache instances require an ID.");
} else {
return this.getId().hashCode();
}
}
}

BlockingCache

BlockingCache是阻塞版本的缓存装饰器,保证只有一个线程到数据库中查找指定key对应的数据

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
public class BlockingCache implements Cache {
//阻塞超时时长
private long timeout;
// 被装饰的底层Cache对象
private final Cache delegate;
//每个key对应一个ReetrantLock
private final ConcurrentHashMap<Object, ReentrantLock> locks;

public BlockingCache(Cache delegate) {
this.delegate = delegate;
this.locks = new ConcurrentHashMap();
}

public String getId() {
return this.delegate.getId();
}

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

public void putObject(Object key, Object value) {
try {
this.delegate.putObject(key, value);
} finally {
this.releaseLock(key);
}

}

public Object getObject(Object key) {
// 获取该key对应的锁
this.acquireLock(key);
//查询key
Object value = this.delegate.getObject(key);
//缓存中有key对应的缓存项才会释放锁,否则会一直持有锁
if (value != null) {
this.releaseLock(key);
}

return value;
}

public Object removeObject(Object key) {
this.releaseLock(key);
return null;
}

public void clear() {
this.delegate.clear();
}

private ReentrantLock getLockForKey(Object key) {
return (ReentrantLock)this.locks.computeIfAbsent(key, (k) -> {
return new ReentrantLock();
});
}

private void acquireLock(Object key) {
Lock lock = this.getLockForKey(key);
if (this.timeout > 0L) {
try {
boolean acquired = lock.tryLock(this.timeout, TimeUnit.MILLISECONDS);
if (!acquired) {
throw new CacheException("Couldn't get a lock in " + this.timeout + " for the key " + key + " at the cache " + this.delegate.getId());
}
} catch (InterruptedException var4) {
throw new CacheException("Got interrupted while trying to acquire lock for key " + key, var4);
}
} else {
lock.lock();
}

}

private void releaseLock(Object key) {
ReentrantLock lock = (ReentrantLock)this.locks.get(key);
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}

}

public long getTimeout() {
return this.timeout;
}

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

FifoCache

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
public class FifoCache implements Cache {
private final Cache delegate;
// 根据key进入缓存的先后顺序
private final Deque<Object> keyList;
// 记录缓存的上限
private int size;

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

public String getId() {
return this.delegate.getId();
}

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

public void setSize(int size) {
this.size = size;
}

public void putObject(Object key, Object value) {
this.cycleKeyList(key);
this.delegate.putObject(key, value);
}

public Object getObject(Object key) {
return this.delegate.getObject(key);
}

public Object removeObject(Object key) {
return this.delegate.removeObject(key);
}

public void clear() {
this.delegate.clear();
this.keyList.clear();
}

//检测并清理缓存
private void cycleKeyList(Object key) {
this.keyList.addLast(key);
if (this.keyList.size() > this.size) {
Object oldestKey = this.keyList.removeFirst();
this.delegate.removeObject(oldestKey);
}

}
}

LruCache

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

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

public String getId() {
return this.delegate.getId();
}

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

public void setSize(final int size) {
//LinkedHashMap的第三个参数true表示记录的顺序是access-order,get方法会改变顺序记录
this.keyMap = new LinkedHashMap<Object, Object>(size, 0.75F, true) {
private static final long serialVersionUID = 4267176411845948333L;
// 调用put方法时会调用该方法
protected boolean removeEldestEntry(Entry<Object, Object> eldest) {
boolean tooBig = this.size() > size;
if (tooBig) {
LruCache.this.eldestKey = eldest.getKey();
}

return tooBig;
}
};
}

public void putObject(Object key, Object value) {
this.delegate.putObject(key, value);
this.cycleKeyList(key);
}

public Object getObject(Object key) {
this.keyMap.get(key);
return this.delegate.getObject(key);
}

public Object removeObject(Object key) {
return this.delegate.removeObject(key);
}

public void clear() {
this.delegate.clear();
this.keyMap.clear();
}

private void cycleKeyList(Object key) {
this.keyMap.put(key, key);
if (this.eldestKey != null) {
this.delegate.removeObject(this.eldestKey);
this.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
public class SoftCache implements Cache {
// 最近使用的一部分缓存不会被GC掉,将值放到该队列中(强引用指向其value)
private final Deque<Object> hardLinksToAvoidGarbageCollection;
//引用队列,用于记录已经被GC回收的缓存项所对应的SoftEntry对象
private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
private final Cache delegate;
private int numberOfHardLinks;

public SoftCache(Cache delegate) {
this.delegate = delegate;
this.numberOfHardLinks = 256;
this.hardLinksToAvoidGarbageCollection = new LinkedList();
this.queueOfGarbageCollectedEntries = new ReferenceQueue();
}

public String getId() {
return this.delegate.getId();
}

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

public void setSize(int size) {
this.numberOfHardLinks = size;
}

public void putObject(Object key, Object value) {
this.removeGarbageCollectedItems();
this.delegate.putObject(key, new SoftCache.SoftEntry(key, value, this.queueOfGarbageCollectedEntries));
}

public Object getObject(Object key) {
Object result = null;
SoftReference<Object> softReference = (SoftReference)this.delegate.getObject(key);
if (softReference != null) {
result = softReference.get();
if (result == null) {
this.delegate.removeObject(key);
} else {
synchronized(this.hardLinksToAvoidGarbageCollection) {
this.hardLinksToAvoidGarbageCollection.addFirst(result);
if (this.hardLinksToAvoidGarbageCollection.size() > this.numberOfHardLinks) {
this.hardLinksToAvoidGarbageCollection.removeLast();
}
}
}
}

return result;
}

public Object removeObject(Object key) {
this.removeGarbageCollectedItems();
return this.delegate.removeObject(key);
}

public void clear() {
synchronized(this.hardLinksToAvoidGarbageCollection) {
this.hardLinksToAvoidGarbageCollection.clear();
}

this.removeGarbageCollectedItems();
this.delegate.clear();
}

private void removeGarbageCollectedItems() {
SoftCache.SoftEntry sv;
while((sv = (SoftCache.SoftEntry)this.queueOfGarbageCollectedEntries.poll()) != null) {
this.delegate.removeObject(sv.key);
}

}

private static class SoftEntry extends SoftReference<Object> {
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
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();
}

public String getId() {
return this.delegate.getId();
}

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

public void setSize(int size) {
this.numberOfHardLinks = size;
}

public void putObject(Object key, Object value) {
this.removeGarbageCollectedItems();
this.delegate.putObject(key, new WeakCache.WeakEntry(key, value, this.queueOfGarbageCollectedEntries));
}

public Object getObject(Object key) {
Object result = null;
WeakReference<Object> weakReference = (WeakReference)this.delegate.getObject(key);
if (weakReference != null) {
result = weakReference.get();
if (result == null) {
this.delegate.removeObject(key);
} else {
this.hardLinksToAvoidGarbageCollection.addFirst(result);
if (this.hardLinksToAvoidGarbageCollection.size() > this.numberOfHardLinks) {
this.hardLinksToAvoidGarbageCollection.removeLast();
}
}
}

return result;
}

public Object removeObject(Object key) {
this.removeGarbageCollectedItems();
return this.delegate.removeObject(key);
}

public void clear() {
this.hardLinksToAvoidGarbageCollection.clear();
this.removeGarbageCollectedItems();
this.delegate.clear();
}

private void removeGarbageCollectedItems() {
WeakCache.WeakEntry sv;
while((sv = (WeakCache.WeakEntry)this.queueOfGarbageCollectedEntries.poll()) != null) {
this.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

LoggingCache在Cache的基础上提供了日志功能,通过hits和request字段记录了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
public class LoggingCache implements Cache {
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(this.getId());
}

public String getId() {
return this.delegate.getId();
}

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

public void putObject(Object key, Object object) {
this.delegate.putObject(key, object);
}

public Object getObject(Object key) {
++this.requests;
Object value = this.delegate.getObject(key);
if (value != null) {
++this.hits;
}

if (this.log.isDebugEnabled()) {
this.log.debug("Cache Hit Ratio [" + this.getId() + "]: " + this.getHitRatio());
}

return value;
}

public Object removeObject(Object key) {
return this.delegate.removeObject(key);
}

public void clear() {
this.delegate.clear();
}

public int hashCode() {
return this.delegate.hashCode();
}

public boolean equals(Object obj) {
return this.delegate.equals(obj);
}
//命中率
private double getHitRatio() {
return (double)this.hits / (double)this.requests;
}
}

ScheduledCache

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
public class ScheduledCache implements Cache {
private final Cache delegate;
// 记录两次缓存清理的时间间隔,默认一小时
protected long clearInterval;
// 记录最后一次清理的时间戳
protected long lastClear;

public ScheduledCache(Cache delegate) {
this.delegate = delegate;
this.clearInterval = TimeUnit.HOURS.toMillis(1L);
this.lastClear = System.currentTimeMillis();
}

public void setClearInterval(long clearInterval) {
this.clearInterval = clearInterval;
}

public String getId() {
return this.delegate.getId();
}

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

public void putObject(Object key, Object object) {
this.clearWhenStale();
this.delegate.putObject(key, object);
}

public Object getObject(Object key) {
return this.clearWhenStale() ? null : this.delegate.getObject(key);
}

public Object removeObject(Object key) {
this.clearWhenStale();
return this.delegate.removeObject(key);
}

public void clear() {
this.lastClear = System.currentTimeMillis();
this.delegate.clear();
}

public int hashCode() {
return this.delegate.hashCode();
}

public boolean equals(Object obj) {
return this.delegate.equals(obj);
}

private boolean clearWhenStale() {
if (System.currentTimeMillis() - this.lastClear > this.clearInterval) {
this.clear();
return true;
} else {
return false;
}
}
}

SerializedCache

SerializedCache提供了将value进行序列化的功能

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
public class SerializedCache implements Cache {
private final Cache delegate;

public SerializedCache(Cache delegate) {
this.delegate = delegate;
}

public String getId() {
return this.delegate.getId();
}

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

public void putObject(Object key, Object object) {
if (object != null && !(object instanceof Serializable)) {
throw new CacheException("SharedCache failed to make a copy of a non-serializable object: " + object);
} else {
this.delegate.putObject(key, this.serialize((Serializable)object));
}
}

public Object getObject(Object key) {
Object object = this.delegate.getObject(key);
return object == null ? null : this.deserialize((byte[])((byte[])object));
}

public Object removeObject(Object key) {
return this.delegate.removeObject(key);
}

public void clear() {
this.delegate.clear();
}

public int hashCode() {
return this.delegate.hashCode();
}

public boolean equals(Object obj) {
return this.delegate.equals(obj);
}

private byte[] serialize(Serializable value) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Throwable var3 = null;

Object var6;
try {
ObjectOutputStream oos = new ObjectOutputStream(bos);
Throwable var5 = null;

try {
oos.writeObject(value);
oos.flush();
var6 = bos.toByteArray();
} catch (Throwable var31) {
var6 = var31;
var5 = var31;
throw var31;
} finally {
if (oos != null) {
if (var5 != null) {
try {
oos.close();
} catch (Throwable var30) {
var5.addSuppressed(var30);
}
} else {
oos.close();
}
}

}
} catch (Throwable var33) {
var3 = var33;
throw var33;
} finally {
if (bos != null) {
if (var3 != null) {
try {
bos.close();
} catch (Throwable var29) {
var3.addSuppressed(var29);
}
} else {
bos.close();
}
}

}

return (byte[])var6;
} catch (Exception var35) {
throw new CacheException("Error serializing object. Cause: " + var35, var35);
}
}

private Serializable deserialize(byte[] value) {
try {
ByteArrayInputStream bis = new ByteArrayInputStream(value);
Throwable var4 = null;

Serializable result;
try {
ObjectInputStream ois = new SerializedCache.CustomObjectInputStream(bis);
Throwable var6 = null;

try {
result = (Serializable)ois.readObject();
} catch (Throwable var31) {
var6 = var31;
throw var31;
} finally {
if (ois != null) {
if (var6 != null) {
try {
ois.close();
} catch (Throwable var30) {
var6.addSuppressed(var30);
}
} else {
ois.close();
}
}

}
} catch (Throwable var33) {
var4 = var33;
throw var33;
} finally {
if (bis != null) {
if (var4 != null) {
try {
bis.close();
} catch (Throwable var29) {
var4.addSuppressed(var29);
}
} else {
bis.close();
}
}

}

return result;
} catch (Exception var35) {
throw new CacheException("Error deserializing object. Cause: " + var35, var35);
}
}

public static class CustomObjectInputStream extends ObjectInputStream {
public CustomObjectInputStream(InputStream in) throws IOException {
super(in);
}

protected Class<?> resolveClass(ObjectStreamClass desc) throws ClassNotFoundException {
return Resources.classForName(desc.getName());
}
}
}

SynchronizedCache

SynchronizedCache为每个方法添加synchronized,添加了同步功能

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
public class SynchronizedCache implements Cache {
private final Cache delegate;

public SynchronizedCache(Cache delegate) {
this.delegate = delegate;
}

public String getId() {
return this.delegate.getId();
}

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

public synchronized void putObject(Object key, Object object) {
this.delegate.putObject(key, object);
}

public synchronized Object getObject(Object key) {
return this.delegate.getObject(key);
}

public synchronized Object removeObject(Object key) {
return this.delegate.removeObject(key);
}

public synchronized void clear() {
this.delegate.clear();
}

public int hashCode() {
return this.delegate.hashCode();
}

public boolean equals(Object obj) {
return this.delegate.equals(obj);
}
}

CacheKey

在Cache中唯一确定一个缓存项使用的key为CacheKey

CacheKey的组成部分

  • MappedStatement的id
  • 指定查询结果集的范围,也就是RowBounds.offset和RowBounds.limit
  • 查询所使用的sql语句,boundSql.getSql(),包含?占位符
  • 用户传递的实际参数

CacheKey就是mappedStementId + offset + limit + SQL + queryParams + environment生成一个哈希码

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
public class CacheKey implements Cloneable, Serializable {
private static final long serialVersionUID = 1146682552656046210L;
public static final CacheKey NULL_CACHE_KEY = new CacheKey() {
public void update(Object object) {
throw new CacheException("Not allowed to update a null cache key instance.");
}

public void updateAll(Object[] objects) {
throw new CacheException("Not allowed to update a null cache key instance.");
}
};
private static final int DEFAULT_MULTIPLIER = 37;
private static final int DEFAULT_HASHCODE = 17;
// 参与计算hashcode,默认为37
private final int multiplier;
// CacheKey的hashcode,默认为17
private int hashcode;
//检验和
private long checksum;
// updateList集合的个数
private int count;
// 由该集合中的所有对象共同决定两个CacheKey是否相同
private List<Object> updateList;

public CacheKey() {
this.hashcode = 17;
this.multiplier = 37;
this.count = 0;
this.updateList = new ArrayList();
}

public CacheKey(Object[] objects) {
this();
this.updateAll(objects);
}

public int getUpdateCount() {
return this.updateList.size();
}
// 向updateList中添加对象时,使用的是update方法
public void update(Object object) {
int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
++this.count;
this.checksum += (long)baseHashCode;
baseHashCode *= this.count;
this.hashcode = this.multiplier * this.hashcode + baseHashCode;
this.updateList.add(object);
}

public void updateAll(Object[] objects) {
Object[] var2 = objects;
int var3 = objects.length;

for(int var4 = 0; var4 < var3; ++var4) {
Object o = var2[var4];
this.update(o);
}

}

// 判断两个cacheKey是否相等
public boolean equals(Object object) {
if (this == object) {
return true;
} else if (!(object instanceof CacheKey)) {
return false;
} else {
CacheKey cacheKey = (CacheKey)object;
if (this.hashcode != cacheKey.hashcode) {
return false;
} else if (this.checksum != cacheKey.checksum) {
return false;
} else if (this.count != cacheKey.count) {
return false;
} else {
for(int i = 0; i < this.updateList.size(); ++i) {
Object thisObject = this.updateList.get(i);
Object thatObject = cacheKey.updateList.get(i);
if (!ArrayUtil.equals(thisObject, thatObject)) {
return false;
}
}

return true;
}
}
}

public int hashCode() {
return this.hashcode;
}

public String toString() {
StringJoiner returnValue = new StringJoiner(":");
returnValue.add(String.valueOf(this.hashcode));
returnValue.add(String.valueOf(this.checksum));
this.updateList.stream().map(ArrayUtil::toString).forEach(returnValue::add);
return returnValue.toString();
}

public CacheKey clone() throws CloneNotSupportedException {
CacheKey clonedCacheKey = (CacheKey)super.clone();
clonedCacheKey.updateList = new ArrayList(this.updateList);
return clonedCacheKey;
}
}

欢迎关注我的其它发布渠道