相关阅读:
【极客源码】JetCache源码(一)开篇
【极客源码】JetCache源码(二)顶层视图
【极客源码】JetCache源码(三)Cache类结构和代码解析1
JAVA基础(一)简单、透彻理解内部类和静态内部类
JAVA基础(二)内存优化-使用Java引用做缓存
JAVA基础(三)ClassLoader实现热加载
JAVA基础(四)枚举(enum)和常量定义,工厂类使用对比
JAVA基础(五)函数式接口-复用,解耦之利刃
JAVA编程思想(一)通过依赖注入增加扩展性
JAVA编程思想(二)如何面向接口编程
JAVA编程思想(三)去掉别扭的if,自注册策略模式优雅满足开闭原则
JAVA编程思想(四)Builder模式经典范式以及和工厂模式如何选?
HikariPool源码(二)设计思想借鉴
人在职场(一)IT大厂生存法则
1. 类结构
1.AbstractEmbeddedCache下面有两个缓存实现,分别是LRU缓存和Caffeine缓存
2.一如既往的,模板模式被使用,只不过这里的模板方法不是直接在子类实现,而是通过子类创建InnerMap实例,再在模板方法的调用点调用InnerMap实例的相应方法,这么做的目的是:让需要子类实现的模板方法都内聚到一个类中,并且相对独立(直接在子类实现也内聚,但是不够独立),当类独立后,后续如果要扩展,可以通过独立的工厂类来完成创建过程,工厂类还可以通过依赖注入,大大提高了灵活性,扩展性。
2. 核心代码解析
2.1. AbstractEmbeddedCache.java
AbstractEmbeddedCache实现了一些公共方法,避免子类重复实现,并且采用模板模式,由子类完成变化点部分的实现。
public abstract class AbstractEmbeddedCache<K, V> extends AbstractCache<K, V> {
// 有自己的配置类
protected EmbeddedCacheConfig<K, V> config;
// 持有InnerMap接口,模板方法都委托给它
protected InnerMap innerMap;
// 由子类来创建InnerMap,使得要调用得模板方法内聚且独立
protected abstract InnerMap createAreaCache();
public AbstractEmbeddedCache(EmbeddedCacheConfig<K, V> config) {
this.config = config;
// 模板方法模式,由子类实现
innerMap = createAreaCache();
}
@Override
public CacheConfig<K, V> config() {
return config;
}
// 公共方法
public Object buildKey(K key) {
Object newKey = key;
Function<K, Object> keyConvertor = config.getKeyConvertor();
if (keyConvertor != null) {
newKey = keyConvertor.apply(key);
}
return newKey;
}
@Override
protected CacheGetResult<V> do_GET(K key) {
Object newKey = buildKey(key);
// 委托给innerMap,也可以是定义一个模板方法,由子类实现,但innerMap可将所有待实现模板方法内聚到一起,也更独立
CacheValueHolder<V> holder = (CacheValueHolder<V>) innerMap.getValue(newKey);
return parseHolderResult(holder);
}
protected CacheGetResult<V> parseHolderResult(CacheValueHolder<V> holder) {
long now = System.currentTimeMillis();
if (holder == null) {
return CacheGetResult.NOT_EXISTS_WITHOUT_MSG;
} else if (now >= holder.getExpireTime()) {
return CacheGetResult.EXPIRED_WITHOUT_MSG;
} else {
synchronized (holder) {
long accessTime = holder.getAccessTime();
if (config.isExpireAfterAccess()) {
long expireAfterAccess = config.getExpireAfterAccessInMillis();
if (now >= accessTime + expireAfterAccess) {
return CacheGetResult.EXPIRED_WITHOUT_MSG;
}
}
holder.setAccessTime(now);
}
return new CacheGetResult(CacheResultCode.SUCCESS, null, holder);
}
}
@Override
protected MultiGetResult<K, V> do_GET_ALL(Set<? extends K> keys) {
ArrayList<K> keyList = new ArrayList<K>(keys.size());
ArrayList<Object> newKeyList = new ArrayList<Object>(keys.size());
keys.stream().forEach((k) -> {
Object newKey = buildKey(k);
keyList.add(k);
newKeyList.add(newKey);
});
// 仍然是委托给innerMap
Map<Object, CacheValueHolder<V>> innerResultMap = innerMap.getAllValues(newKeyList);
Map<K, CacheGetResult<V>> resultMap = new HashMap<>();
for (int i = 0; i < keyList.size(); i++) {
K key = keyList.get(i);
Object newKey = newKeyList.get(i);
CacheValueHolder<V> holder = innerResultMap.get(newKey);
resultMap.put(key, parseHolderResult(holder));
}
MultiGetResult<K, V> result = new MultiGetResult<>(CacheResultCode.SUCCESS, null, resultMap);
return result;
}
// 后面几个方法也都是公共代码+模板方法,委托给innerMap调用,不在详述
复制代码
2.2. LinkedHashMapCache.java
2.2.1. 要点
// 定义一个成员变量
private LRUMap lruMap;
@Override
protected InnerMap createAreaCache() {
// 先赋值给私有成员,再返回.
lruMap = new LRUMap(config.getLimit(), this);
return lruMap;
}
public void cleanExpiredEntry() {
// 这样不需要强转
lruMap.cleanExpiredEntry();
}
复制代码
2.2.2. 代码
package com.alicp.jetcache.embedded;
import com.alicp.jetcache.CacheResultCode;
import com.alicp.jetcache.CacheValueHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
/**
* @author <a href="mailto:areyouok@gmail.com">huangli</a>
*/
public class LinkedHashMapCache<K, V> extends AbstractEmbeddedCache<K, V> {
private static Logger logger = LoggerFactory.getLogger(LinkedHashMapCache.class);
public LinkedHashMapCache(EmbeddedCacheConfig<K, V> config) {
super(config);
addToCleaner();
}
protected void addToCleaner() {
Cleaner.add(this);
}
@Override
protected InnerMap createAreaCache() {
// 这里先定义一个私有的LRUMap成员,先赋值给私有成员,再返回,下面的cleanExpiredEntry方法中就不用强转
return new LRUMap(config.getLimit(), this);
}
@Override
public <T> T unwrap(Class<T> clazz) {
if (clazz.equals(LinkedHashMap.class)) {
return (T) innerMap;
}
throw new IllegalArgumentException(clazz.getName());
}
public void cleanExpiredEntry() {
// 面向接口编程时,由于子类的方法比接口多,被迫使用强转总感觉有些别扭
// 要避免强转,可以定义一个私有的LRUMap成员,在创建InnerMap时,同时复制给它,这里通过LRUMap的私有人员来调用
((LRUMap) innerMap).cleanExpiredEntry();
}
// 内部类
final class LRUMap extends LinkedHashMap implements InnerMap {
private final int max;
// 可以直接定义在LinkedHashMapCache中并直接引用,不需要通过构造器传入
private Object lock;
// 从实际调用情况来看,这个lock是LinkedHashMapCache类实例,之所以用LinkedHashMapCache类实例,
// 是想基于LinkedHashMapCache实例加锁,尽管当前实际只是在LRUMap实例方法中用到了加锁,为了避免后续扩展,
// 基于最小可见性原则,也可以直接在LinkedHashMapCache中定义一个锁来使用,而不需要通过构造器传入。
public LRUMap(int max, Object lock) {
super((int) (max * 1.4f), 0.75f, true);
this.max = max;
this.lock = lock;
}
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > max;
}
// 清理失效缓存
void cleanExpiredEntry() {
synchronized (lock) {
for (Iterator it = entrySet().iterator(); it.hasNext();) {
Map.Entry en = (Map.Entry) it.next();
Object value = en.getValue();
if (value != null && value instanceof CacheValueHolder) {
CacheValueHolder h = (CacheValueHolder) value;
logger.info("CacheValueHolder: " + h.toString());
if (System.currentTimeMillis() >= h.getExpireTime()) {
it.remove();
}
} else {
// assert false
if (value == null) {
logger.error("key " + en.getKey() + " is null");
} else {
logger.error("value of key " + en.getKey() + " is not a CacheValueHolder. type=" + value.getClass());
}
}
}
}
}
@Override
public Object getValue(Object key) {
synchronized (lock) {
return get(key);
}
}
@Override
public Map getAllValues(Collection keys) {
Map values = new HashMap();
synchronized (lock) {
for (Object key : keys) {
Object v = get(key);
if (v != null) {
values.put(key, v);
}
}
}
return values;
}
@Override
public void putValue(Object key, Object value) {
synchronized (lock) {
put(key, value);
}
}
@Override
public void putAllValues(Map map) {
synchronized (lock) {
Set<Map.Entry> set = map.entrySet();
for (Map.Entry en : set) {
put(en.getKey(), en.getValue());
}
}
}
@Override
public boolean removeValue(Object key) {
synchronized (lock) {
return remove(key) != null;
}
}
@Override
public void removeAllValues(Collection keys) {
synchronized (lock) {
for (Object k : keys) {
remove(k);
}
}
}
@Override
@SuppressWarnings("unchecked")
public boolean putIfAbsentValue(Object key, Object value) {
synchronized (lock) {
CacheValueHolder h = (CacheValueHolder) get(key);
if (h == null || parseHolderResult(h).getResultCode() == CacheResultCode.EXPIRED) {
put(key, value);
return true;
} else {
return false;
}
}
}
}
}
复制代码
2.3. CaffeineCache.java
2.3.1. 要点
2.3.2. 代码
package com.alicp.jetcache.embedded;
import com.alicp.jetcache.CacheValueHolder;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Expiry;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Created on 2016/10/25.
*
* @author <a href="mailto:areyouok@gmail.com">huangli</a>
*/
public class CaffeineCache<K, V> extends AbstractEmbeddedCache<K, V> {
// 持有caffeine的缓存实例,方法调用最终都委托给它
private com.github.benmanes.caffeine.cache.Cache cache;
public CaffeineCache(EmbeddedCacheConfig<K, V> config) {
super(config);
}
@Override
public <T> T unwrap(Class<T> clazz) {
if (clazz.equals(com.github.benmanes.caffeine.cache.Cache.class)) {
return (T) cache;
}
throw new IllegalArgumentException(clazz.getName());
}
@Override
@SuppressWarnings("unchecked")
protected InnerMap createAreaCache() {
// Builder设计模式
Caffeine<Object, Object> builder = Caffeine.newBuilder();
builder.maximumSize(config.getLimit());
final boolean isExpireAfterAccess = config.isExpireAfterAccess();
final long expireAfterAccess = config.getExpireAfterAccessInMillis();
// 通过依赖注入重载默认实现
builder.expireAfter(new Expiry<Object, CacheValueHolder>() {
private long getRestTimeInNanos(CacheValueHolder value) {
long now = System.currentTimeMillis();
long ttl = value.getExpireTime() - now;
if(isExpireAfterAccess){
ttl = Math.min(ttl, expireAfterAccess);
}
return TimeUnit.MILLISECONDS.toNanos(ttl);
}
@Override
public long expireAfterCreate(Object key, CacheValueHolder value, long currentTime) {
return getRestTimeInNanos(value);
}
@Override
public long expireAfterUpdate(Object key, CacheValueHolder value,
long currentTime, long currentDuration) {
return currentDuration;
}
@Override
public long expireAfterRead(Object key, CacheValueHolder value,
long currentTime, long currentDuration) {
return getRestTimeInNanos(value);
}
});
cache = builder.build();
// 因为调用关系比较简单,使用了匿名内部类
return new InnerMap() {
@Override
public Object getValue(Object key) {
return cache.getIfPresent(key);
}
@Override
public Map getAllValues(Collection keys) {
return cache.getAllPresent(keys);
}
@Override
public void putValue(Object key, Object value) {
cache.put(key, value);
}
@Override
public void putAllValues(Map map) {
cache.putAll(map);
}
@Override
public boolean removeValue(Object key) {
return cache.asMap().remove(key) != null;
}
@Override
public void removeAllValues(Collection keys) {
cache.invalidateAll(keys);
}
@Override
public boolean putIfAbsentValue(Object key, Object value) {
return cache.asMap().putIfAbsent(key, value) == null;
}
};
}
}
复制代码
3. 总结
1. 在使用模板方法设计模式时,可以将所有待实现模板方法内聚到一个类中,即内聚又相对独立,再加上可注入的工厂类,可进一步提高扩展性。
2. 依赖注入随处可见,是提供高扩展性的必备模式。
3. 既要面向接口编程,又要避免强转,实现方式值得思考。
4. Builder模式相比工厂模式创建实例,也很常见,属于必会设计模式。
end.