Freemarker源码分析(3)cache.CacheStorage及其子类

2021SC@SDUSC

Freemarker源码分析(3)cache.CacheStorage及其子类

cache.CacheStorage及其子类

总览图
Freemarker源码分析(3)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 中文官方参考手册

新手写的代码分析,文章若有错误还请指出

上一篇:3.Springboot(三)


下一篇:FreeMarker使用模板生成Json数据