标签:实现 equal ado set register fork java 元素 settime
Mybatis中如何配置二级缓存
Cache解析处理过程
@CacheNamespace(blocking=true)
public interface PersonMapper {
@Select("select id, firstname, lastname from person")
public List<Person> findAll();
}
<mapper namespace="org.apache.ibatis.submitted.cacheorder.Mapper2"> <cache/> </mapper>

为什么配置了一个<cache/>就可以使用缓存了呢?通过下面的源码可以发现,缓存配置是有默认值的
private void cacheElement(XNode context) throws Exception {
if (context != null) {
//获取配置的type值,默认值为PERPETUAL
String type = context.getStringAttribute("type", "PERPETUAL");
//获取type的class
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
//获取配置过期策略,默认值为LRU
String eviction = context.getStringAttribute("eviction", "LRU");
Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
//获取配置的刷新间隔
Long flushInterval = context.getLongAttribute("flushInterval");
//获取配置的缓存大小
Integer size = context.getIntAttribute("size");
//是否配置了只读,默认为false
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
//是否配置了阻塞,默认为false
boolean blocking = context.getBooleanAttribute("blocking", false);
Properties props = context.getChildrenAsProperties();
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
}
}
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
缓存的基本实现
public class PerpetualCache implements Cache {
//缓存ID
private final String id;
//缓存
private Map<Object, Object> cache = new HashMap<Object, Object>();
public PerpetualCache(String id) {
this.id = id;
}
@Override
public String getId() {
return id;
}
@Override
public int getSize() {
return cache.size();
}
@Override
public void putObject(Object key, Object value) {
cache.put(key, value);
}
@Override
public Object getObject(Object key) {
return cache.get(key);
}
@Override
public Object removeObject(Object key) {
return cache.remove(key);
}
@Override
public void clear() {
cache.clear();
}
@Override
public ReadWriteLock getReadWriteLock() {
return null;
}
@Override
public boolean equals(Object o) {
if (getId() == null) {
throw new CacheException("Cache instances require an ID.");
}
if (this == o) {
return true;
}
if (!(o instanceof Cache)) {
return false;
}
Cache otherCache = (Cache) o;
return getId().equals(otherCache.getId());
}
@Override
public int hashCode() {
if (getId() == null) {
throw new CacheException("Cache instances require an ID.");
}
return getId().hashCode();
}
}
在Mybatis中是不是根据不通的过期策略都创建不通都缓存呢?实际上Mybatis的所有Cache算法都是基于装饰器模式对PerpetualCache扩展增加功能。下面对其装饰器源码进行分析
/**
* 简单阻塞装饰器
*
* 当前缓存中不存在时对缓存缓存的key加锁,其它线程就只能一直等到这个元素保存到缓存中
* 由于对每个key都保存了锁对象,如果在大量查询中使用可能存在OOM都风险
* @author Eduardo Macarron
*
*/
public class BlockingCache implements Cache {
//超时时间
private long timeout;
//委派代表
private final Cache delegate;
//缓存key和锁的映射关系
private final ConcurrentHashMap<Object, ReentrantLock> locks;
public BlockingCache(Cache delegate) {
this.delegate = delegate;
this.locks = new ConcurrentHashMap<Object, ReentrantLock>();
}
//获取ID,直接委派给delegate处理
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
return delegate.getSize();
}
//放置缓存,结束后释放锁; 注意在方缓存前是没有加锁的
//该处设置是和获取缓存有很大关系
@Override
public void putObject(Object key, Object value) {
try {
delegate.putObject(key, value);
} finally {
releaseLock(key);
}
}
@Override
public Object getObject(Object key) {
//获取锁
acquireLock(key);
//获取缓存数据
Object value = delegate.getObject(key);
//如果缓存数据存在则释放锁,否则返回,注意,此时锁没有释放;下一个线程获取的时候是没有办法
//获取锁,只能等待;记住 put结束的时候会释放锁,这里就是为什么put之前没有获取锁,但是结束后要释放锁的原因
if (value != null) {
releaseLock(key);
}
return value;
}
@Override
public Object removeObject(Object key) {
// despite of its name, this method is called only to release locks
releaseLock(key);
return null;
}
@Override
public void clear() {
delegate.clear();
}
@Override
public ReadWriteLock getReadWriteLock() {
return null;
}
private ReentrantLock getLockForKey(Object key) {
ReentrantLock lock = new ReentrantLock();
ReentrantLock previous = locks.putIfAbsent(key, lock);
return previous == null ? lock : previous;
}
private void acquireLock(Object key) {
Lock lock = getLockForKey(key);
if (timeout > 0) {
try {
boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);
if (!acquired) {
throw new CacheException("Couldn‘t get a lock in " + timeout + " for the key " + key + " at the cache " + delegate.getId());
}
} catch (InterruptedException e) {
throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);
}
} else {
lock.lock();
}
}
private void releaseLock(Object key) {
ReentrantLock lock = locks.get(key);
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
public long getTimeout() {
return timeout;
}
public void setTimeout(long timeout) {
this.timeout = timeout;
}
}
public class LruCache implements Cache {
private final Cache delegate;
//key映射表
private Map<Object, Object> keyMap;
//最老的key
private Object eldestKey;
public LruCache(Cache delegate) {
this.delegate = delegate;
setSize(1024);
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
return delegate.getSize();
}
public void setSize(final int size) {
//使用LinedListHashMap实现LRU, accessOrder=true 会按照访问顺序排序,最近访问的放在最前,最早访问的放在后面
keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
private static final long serialVersionUID = 4267176411845948333L;
@Override
protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
//如果当前大小已经超过1024则删除最老元素
boolean tooBig = size() > size;
if (tooBig) {
//将最老元素赋值给eldestKey
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) {
//每次反问都会触发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();
}
@Override
public ReadWriteLock getReadWriteLock() {
return null;
}
private void cycleKeyList(Object key) {
//将当前key放入keyMap
keyMap.put(key, key);
//如果最老的key不为null则清除最老的key的缓存
if (eldestKey != null) {
delegate.removeObject(eldestKey);
eldestKey = null;
}
}
}
public class FifoCache implements Cache {
private final Cache delegate;
//双端队列
private final Deque<Object> keyList;
private int size;
public FifoCache(Cache delegate) {
this.delegate = delegate;
this.keyList = new LinkedList<Object>();
this.size = 1024;
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
return delegate.getSize();
}
public void setSize(int size) {
this.size = size;
}
@Override
public void putObject(Object key, Object value) {
cycleKeyList(key);
delegate.putObject(key, value);
}
@Override
public Object getObject(Object key) {
return delegate.getObject(key);
}
@Override
public Object removeObject(Object key) {
return delegate.removeObject(key);
}
@Override
public void clear() {
delegate.clear();
keyList.clear();
}
@Override
public ReadWriteLock getReadWriteLock() {
return null;
}
private void cycleKeyList(Object key) {
//将当前key添加到队尾
keyList.addLast(key);
//如果key的队列长度超过限制则删除队首的key以及缓存
if (keyList.size() > size) {
Object oldestKey = keyList.removeFirst();
delegate.removeObject(oldestKey);
}
}
}
标签:实现 equal ado set register fork java 元素 settime
原文地址:https://www.cnblogs.com/wei-zw/p/8903977.html