2021SC@SDUSC
Freemarker源码分析(3)cache.CacheStorage及其子类
cache.CacheStorage及其子类
总览图
1.CacheStorage接口
代码
public interface CacheStorage {
public Object get(Object key);
public void put(Object key, Object value);
public void remove(Object key);
public void clear();
}
作用:Cache storage抽象了高速缓存的存储方面——将对象(object)与键(key)相关联,通过键进行检索和删除。它实际上是java.util.Map的一个子接口。
2.CacheStorageWithGetSize接口
代码
public interface CacheStorageWithGetSize extends CacheStorage {
int getSize();
}
作用:具有getSize()方法的缓存存储,用于返回当前缓存条目的数量。
3.ConcurrentCacheStorage接口
代码
public interface ConcurrentCacheStorage extends CacheStorage {
public boolean isConcurrent();
}
作用:一个可选的缓存存储接口,它知道是否可以在不同步的情况下并发访问。
4.NullCacheStorage类
代码
public class NullCacheStorage implements ConcurrentCacheStorage, CacheStorageWithGetSize {
public static final NullCacheStorage INSTANCE = new NullCacheStorage();
@Override
public boolean isConcurrent() {
return true;
}
@Override
public Object get(Object key) {
return null;
}
@Override
public void put(Object key, Object value) {
// do nothing
}
@Override
public void remove(Object key) {
// do nothing
}
@Override
public void clear() {
// do nothing
}
public int getSize() {
return 0;
}
}
作用:一个不存储任何东西的高速缓存存储器。如果不需要缓存,可以使用这个。
5.SoftCacheStorage类
代码
public class SoftCacheStorage implements ConcurrentCacheStorage, CacheStorageWithGetSize {
private static final Method atomicRemove = getAtomicRemoveMethod();
private final ReferenceQueue queue = new ReferenceQueue();
private final Map map;
private final boolean concurrent;
public SoftCacheStorage() {
this(new ConcurrentHashMap());
}
@Override
public boolean isConcurrent() {
return concurrent;
}
public SoftCacheStorage(Map backingMap) {
map = backingMap;
this.concurrent = map instanceof ConcurrentMap;
}
@Override
public Object get(Object key) {
processQueue();
Reference ref = (Reference) map.get(key);
return ref == null ? null : ref.get();
}
@Override
public void put(Object key, Object value) {
processQueue();
map.put(key, new SoftValueReference(key, value, queue));
}
@Override
public void remove(Object key) {
processQueue();
map.remove(key);
}
@Override
public void clear() {
map.clear();
processQueue();
}
@Override
public int getSize() {
processQueue();
return map.size();
}
private void processQueue() {
for (; ; ) {
SoftValueReference ref = (SoftValueReference) queue.poll();
if (ref == null) {
return;
}
Object key = ref.getKey();
if (concurrent) {
try {
atomicRemove.invoke(map, new Object[] { key, ref });
} catch (IllegalAccessException | InvocationTargetException e) {
throw new UndeclaredThrowableException(e);
}
} else if (map.get(key) == ref) {
map.remove(key);
}
}
}
private static final class SoftValueReference extends SoftReference {
private final Object key;
SoftValueReference(Object key, Object value, ReferenceQueue queue) {
super(value, queue);
this.key = key;
}
Object getKey() {
return key;
}
}
private static Method getAtomicRemoveMethod() {
try {
return Class.forName("java.util.concurrent.ConcurrentMap").getMethod("remove", new Class[] { Object.class, Object.class });
} catch (ClassNotFoundException e) {
return null;
} catch (NoSuchMethodException e) {
throw new UndeclaredThrowableException(e);
}
}
}
作用:软缓存存储是一种使用SoftReference对象来保存传递给它的对象的缓存存储,因此允许垃圾收集器在确定想要释放内存时清除缓存。这个类的线程安全程度与其底层映射相同。无参数构造函数使用自2.3.24或Java 5以上的线程安全映射。
6.StrongCacheStorage类
代码
public class StrongCacheStorage implements ConcurrentCacheStorage, CacheStorageWithGetSize {
private final Map map = new ConcurrentHashMap();
@Override
public boolean isConcurrent() {
return true;
}
@Override
public Object get(Object key) {
return map.get(key);
}
@Override
public void put(Object key, Object value) {
map.put(key, value);
}
@Override
public void remove(Object key) {
map.remove(key);
}
@Override
public int getSize() {
return map.size();
}
@Override
public void clear() {
map.clear();
}
}
作用:强缓存存储是简单封装Map的缓存存储。它持有对传递给它的所有对象的强引用,因此可以防止在垃圾收集期间清除缓存。
7.MruCacheStorage类
代码
public class MruCacheStorage implements CacheStorageWithGetSize {
private final MruEntry strongHead = new MruEntry();
private final MruEntry softHead = new MruEntry();
{
softHead.linkAfter(strongHead);
}
private final Map map = new HashMap();
private final ReferenceQueue refQueue = new ReferenceQueue();
private final int strongSizeLimit;
private final int softSizeLimit;
private int strongSize = 0;
private int softSize = 0;
public MruCacheStorage(int strongSizeLimit, int softSizeLimit) {
if (strongSizeLimit < 0) throw new IllegalArgumentException("strongSizeLimit < 0");
if (softSizeLimit < 0) throw new IllegalArgumentException("softSizeLimit < 0");
this.strongSizeLimit = strongSizeLimit;
this.softSizeLimit = softSizeLimit;
}
@Override
public Object get(Object key) {
removeClearedReferences();
MruEntry entry = (MruEntry) map.get(key);
if (entry == null) {
return null;
}
relinkEntryAfterStrongHead(entry, null);
Object value = entry.getValue();
if (value instanceof MruReference) {
// This can only happen with strongSizeLimit == 0
return ((MruReference) value).get();
}
return value;
}
@Override
public void put(Object key, Object value) {
removeClearedReferences();
MruEntry entry = (MruEntry) map.get(key);
if (entry == null) {
entry = new MruEntry(key, value);
map.put(key, entry);
linkAfterStrongHead(entry);
} else {
relinkEntryAfterStrongHead(entry, value);
}
}
@Override
public void remove(Object key) {
removeClearedReferences();
removeInternal(key);
}
private void removeInternal(Object key) {
MruEntry entry = (MruEntry) map.remove(key);
if (entry != null) {
unlinkEntryAndInspectIfSoft(entry);
}
}
@Override
public void clear() {
strongHead.makeHead();
softHead.linkAfter(strongHead);
map.clear();
strongSize = softSize = 0;
// Quick refQueue processing
while (refQueue.poll() != null);
}
private void relinkEntryAfterStrongHead(MruEntry entry, Object newValue) {
if (unlinkEntryAndInspectIfSoft(entry) && newValue == null) {
// Turn soft reference into strong reference, unless is was cleared
MruReference mref = (MruReference) entry.getValue();
Object strongValue = mref.get();
if (strongValue != null) {
entry.setValue(strongValue);
linkAfterStrongHead(entry);
} else {
map.remove(mref.getKey());
}
} else {
if (newValue != null) {
entry.setValue(newValue);
}
linkAfterStrongHead(entry);
}
}
private void linkAfterStrongHead(MruEntry entry) {
entry.linkAfter(strongHead);
if (strongSize == strongSizeLimit) {
// softHead.previous is LRU strong entry
MruEntry lruStrong = softHead.getPrevious();
// Attila: This is equaivalent to strongSizeLimit != 0
// DD: But entry.linkAfter(strongHead) was just executed above, so
// lruStrong != strongHead is true even if strongSizeLimit == 0.
if (lruStrong != strongHead) {
lruStrong.unlink();
if (softSizeLimit > 0) {
lruStrong.linkAfter(softHead);
lruStrong.setValue(new MruReference(lruStrong, refQueue));
if (softSize == softSizeLimit) {
// List is circular, so strongHead.previous is LRU soft entry
MruEntry lruSoft = strongHead.getPrevious();
lruSoft.unlink();
map.remove(lruSoft.getKey());
} else {
++softSize;
}
} else {
map.remove(lruStrong.getKey());
}
}
} else {
++strongSize;
}
}
private boolean unlinkEntryAndInspectIfSoft(MruEntry entry) {
entry.unlink();
if (entry.getValue() instanceof MruReference) {
--softSize;
return true;
} else {
--strongSize;
return false;
}
}
private void removeClearedReferences() {
for (; ; ) {
MruReference ref = (MruReference) refQueue.poll();
if (ref == null) {
break;
}
removeInternal(ref.getKey());
}
}
public int getStrongSizeLimit() {
return strongSizeLimit;
}
public int getSoftSizeLimit() {
return softSizeLimit;
}
public int getStrongSize() {
return strongSize;
}
public int getSoftSize() {
removeClearedReferences();
return softSize;
}
@Override
public int getSize() {
return getSoftSize() + getStrongSize();
}
private static final class MruEntry {
private MruEntry prev;
private MruEntry next;
private final Object key;
private Object value;
MruEntry() {
makeHead();
key = value = null;
}
MruEntry(Object key, Object value) {
this.key = key;
this.value = value;
}
Object getKey() {
return key;
}
Object getValue() {
return value;
}
void setValue(Object value) {
this.value = value;
}
MruEntry getPrevious() {
return prev;
}
void linkAfter(MruEntry entry) {
next = entry.next;
entry.next = this;
prev = entry;
next.prev = this;
}
void unlink() {
next.prev = prev;
prev.next = next;
prev = null;
next = null;
}
void makeHead() {
prev = next = this;
}
}
private static class MruReference extends SoftReference {
private final Object key;
MruReference(MruEntry entry, ReferenceQueue queue) {
super(entry.getValue(), queue);
this.key = entry.getKey();
}
Object getKey() {
return key;
}
}
}
作用:实现两级最近使用缓存的缓存存储。在第一层中,项被强引用到指定的最大值。当超过最大值时,最近最少使用的项被移动到第二级缓存,在那里它们被软引用,直到另一个第二级缓存,在那里它们被软引用,直到另一个最近使用的项被完全丢弃。这个缓存存储是StrongCacheStorage和SoftCacheStorage的一般化-两者的效果都可以通过设置一个最大值为零,另一个最大值为最大的正整数来实现。
注:Freemarker代码来自FreeMarker 中文官方参考手册
新手写的代码分析,文章若有错误还请指出