目录
- Mybatis中如何配置二级缓存
- Cache解析处理过程
- Cache支持的过期策略
- 装饰器源码
Mybatis中如何配置二级缓存
基于注解配置缓存
@CacheNamespace(blocking=true) public interface PersonMapper { @Select("select id, firstname, lastname from person") public List<Person> findAll(); }
基于XML配置缓存
<mapper namespace="org.apache.ibatis.submitted.cacheorder.Mapper2"> <cache/> </mapper>
Cache解析处理过程
为什么配置了一个<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); } }
Cache支持的过期策略
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); } } }
原文地址:https://www.cnblogs.com/wei-zw/p/8903977.html
时间: 2024-11-08 18:49:06