MyBatis的二级缓存以及装饰器模式运用

121 阅读20分钟
原文链接: www.cnblogs.com

目录

  1. Mybatis中如何配置二级缓存

  2. Cache解析处理过程

  3. Cache支持的过期策略
  4. 装饰器模式
  5. 装饰器源码

Mybatis中如何配置二级缓存

基于注解配置缓存

?
1 2 3 4 5 6 @CacheNamespace(blocking=true) public interface PersonMapper {     @Select("select id, firstname, lastname from person" )   public List<Person> findAll(); }

 

基于XML配置缓存

?
1 2 3 4 5 <mapper namespace="org.apache.ibatis.submitted.cacheorder.Mapper2">       <cache/>   </mapper>

 

Cache解析处理过程

 

为什么配置了一个<cache/>就可以使用缓存了呢?通过下面的源码可以发现,缓存配置是有默认值的

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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支持的过期策略

?
1 2 3 4 typeAliasRegistry.registerAlias("FIFO", FifoCache.class); typeAliasRegistry.registerAlias("LRU", LruCache.class); typeAliasRegistry.registerAlias("SOFT", SoftCache.class); typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

  缓存的基本实现

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 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扩展增加功能。下面简单介绍一下装饰器(Decorator)模式以及在Mybatis装饰器实现源码

 装饰器模式

     装饰器模式以客户透明对方式动态给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰器模式可以在不创造更多子类的情况下对对象对功能加以扩展。

     装饰器模式常常被称为包裹模式,就是因为每一个装饰器实现类,都是将下一个装饰器或真实实现包裹起来。这样做可以将真实实现简化逻辑,同时更容易扩展新功能。

 Mybatis装饰器

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 private Cache setStandardDecorators(Cache cache) {     try {       MetaObject metaCache = SystemMetaObject.forObject(cache);       if (size != null && metaCache.hasSetter( "size")) {         metaCache.setValue("size", size);       }               if (clearInterval != null) {         //在cache基础上加上ScheduledCache装饰器         cache = new ScheduledCache(cache);         ((ScheduledCache) cache).setClearInterval(clearInterval);       }       //如果配置只读则增加序列化功能       if (readWrite) {         //增加序列化装饰器         cache = new SerializedCache(cache);       }       //增加日志装饰器       cache = new LoggingCache(cache);       //增加同步装饰器       cache = new SynchronizedCache(cache);       if (blocking) {         //增加阻塞读装饰器         cache = new BlockingCache(cache);       }       return cache;     } catch (Exception e) {       throw new CacheException("Error building standard cache decorators.  Cause: " + e, e);     }   }

  

简单阻塞装饰器, 当前缓存中不存在时对缓存缓存的key加锁,其它线程就只能一直等到这个元素保存到缓存中由于对每个key都保存了锁对象,如果在大量查询中使用可能存在OOM都风险

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 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;    }

  LRU缓存,LRU算法主要通过LinkedHashMap实现,实现简单明了

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 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;     }   }   }

  FIFO缓存,通过LinkedList实现了FIFO算法

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 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);     }   }   }

  序列化缓存装饰器,对于缓存对象进行了序列化和反序列化避免了值引用问题

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 /**  * 序列化缓存  */ public class SerializedCache implements Cache {     private final Cache delegate;     public SerializedCache(Cache delegate) {     this.delegate = delegate;   }     @Override   public String getId() {     return delegate.getId();   }     @Override   public int getSize() {     return delegate.getSize();   }     //将数据添加到缓存对时候对数据进行序列化保存   @Override   public void putObject(Object key, Object object) {     //如果对象为null或者实现了Serializable接口的对象需要进行序列化,否则抛出异常     if (object == null || object instanceof Serializable) {       delegate.putObject(key, serialize((Serializable) object));     } else {       throw new CacheException("SharedCache failed to make a copy of a non-serializable object: " + object);     }   }     //从缓存中获取数据对数据进行一次反序列化   @Override   public Object getObject(Object key) {     //从缓存中获取对象     Object object = delegate.getObject(key);     //如果对象为null则直接返回null,否则返回反序列化后对象     return object == null ? null : deserialize((byte[]) object);   }     @Override   public Object removeObject(Object key) {     return delegate.removeObject(key);   }     @Override   public void clear() {     delegate.clear();   }     @Override   public ReadWriteLock getReadWriteLock() {     return null;   }     @Override   public int hashCode() {     return delegate.hashCode();   }     @Override   public boolean equals(Object obj) {     return delegate.equals(obj);   }     //对数据进行序列化   private byte[] serialize(Serializable value) {     try {       ByteArrayOutputStream bos = new ByteArrayOutputStream();       ObjectOutputStream oos = new ObjectOutputStream(bos);       oos.writeObject(value);       oos.flush();       oos.close();       return bos.toByteArray();     } catch (Exception e) {       throw new CacheException("Error serializing object.  Cause: " + e, e);     }   }   //对数据进行反序列化   private Serializable deserialize(byte[] value) {     Serializable result;     try {       ByteArrayInputStream bis = new ByteArrayInputStream(value);       ObjectInputStream ois = new CustomObjectInputStream(bis);       result = (Serializable) ois.readObject();       ois.close();     } catch (Exception e) {       throw new CacheException("Error deserializing object.  Cause: " + e, e);     }     return result;   }     public static class CustomObjectInputStream extends ObjectInputStream {       public CustomObjectInputStream(InputStream in) throws IOException {       super(in);     }       @Override     protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {       return Resources.classForName(desc.getName());     }         }   }

  软引用缓存装饰器,很好的一个实现方式,在弱引用缓存装饰器中同样使用了该方式实现

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 public class SoftCache implements Cache {   //强引用队列,包装队列中的元素不会被垃圾回收   private final Deque<Object> hardLinksToAvoidGarbageCollection;   //引用队列,被回收对象在添加到引用队列中   private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;   //被包装的实现   private final Cache delegate;   //保存强引用的数量   private int numberOfHardLinks;     public SoftCache(Cache delegate) {     this.delegate = delegate;     this.numberOfHardLinks = 256;     this.hardLinksToAvoidGarbageCollection = new LinkedList<Object>();     this.queueOfGarbageCollectedEntries = new ReferenceQueue<Object>();   }     @Override   public String getId() {     return delegate.getId();   }     @Override   public int getSize() {     //返回删除被回收的对象后缓存的大小     removeGarbageCollectedItems();     return delegate.getSize();   }       public void setSize(int size) {     this.numberOfHardLinks = size;   }     @Override   public void putObject(Object key, Object value) {     //删除被回收对象缓存     removeGarbageCollectedItems();     //将当前键值对包装成SoftEntry存入缓存     delegate.putObject(key, new SoftEntry(key, value, queueOfGarbageCollectedEntries));   }     @Override   public Object getObject(Object key) {     Object result = null;     //从缓存中获取软引用对象     SoftReference<Object> softReference = (SoftReference<Object>) delegate.getObject(key);     //如果软引用不为null,则获取引用对象中的真实的对象     if (softReference != null) {       result = softReference.get();       //如果真实的对象为null,则标示该对象已经被垃圾回收了,则删除缓存       if (result == null) {         delegate.removeObject(key);       } else { //真实对象不为null         synchronized (hardLinksToAvoidGarbageCollection) {           //将对象添加早强引用的对头           hardLinksToAvoidGarbageCollection.addFirst(result);           //如果强引用队列达到阈值则删除队尾元素           if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {             hardLinksToAvoidGarbageCollection.removeLast();           }         }       }     }     return result;   }     @Override   public Object removeObject(Object key) {     //删除对象前需要执行删除垃圾回收对象     removeGarbageCollectedItems();     return delegate.removeObject(key);   }     @Override   public void clear() {     synchronized (hardLinksToAvoidGarbageCollection) {       hardLinksToAvoidGarbageCollection.clear();     }     removeGarbageCollectedItems();     delegate.clear();   }     @Override   public ReadWriteLock getReadWriteLock() {     return null;   }     //删除被垃圾回收对象   private void removeGarbageCollectedItems() {     SoftEntry sv;     while ((sv = (SoftEntry) queueOfGarbageCollectedEntries.poll()) != null ) {       delegate.removeObject(sv.key);     }   }     private static class SoftEntry extends SoftReference<Object> {     private final Object key;       SoftEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {       super(value, garbageCollectionQueue);       this.key = key;     }   }   }

  定期清空缓存装饰器,在该装饰器中并不是使用主动失效方式,而是使用懒惰方式。

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 /**  * 定期清空缓存的装饰器,其清空缓存的策略使用的是懒惰清空方式  * 在 getSize,putObject, getObject removeObject的时候会触发清空检查  *  */ public class ScheduledCache implements Cache {     private final Cache delegate;   //刷新间隔   protected long clearInterval;       //最后一次清空缓存时间   protected long lastClear;     public ScheduledCache(Cache delegate) {     this.delegate = delegate;     this.clearInterval = 60 * 60 * 1000; // 1 hour     this.lastClear = System.currentTimeMillis();   }     public void setClearInterval(long clearInterval) {     this.clearInterval = clearInterval;   }     @Override   public String getId() {     return delegate.getId();   }     @Override   public int getSize() {     clearWhenStale();     return delegate.getSize();   }     @Override   public void putObject(Object key, Object object) {     clearWhenStale();     delegate.putObject(key, object);   }     @Override   public Object getObject(Object key) {     return clearWhenStale() ? null : delegate.getObject(key);   }     @Override   public Object removeObject(Object key) {     clearWhenStale();     return delegate.removeObject(key);   }     @Override   public void clear() {     lastClear = System.currentTimeMillis();     delegate.clear();   }     @Override   public ReadWriteLock getReadWriteLock() {     return null;   }     @Override   public int hashCode() {     return delegate.hashCode();   }     @Override   public boolean equals(Object obj) {     return delegate.equals(obj);   }     private boolean clearWhenStale() {     //如果当前时间减去最后一次刷新时间大于刷新间隔则需要晴空缓存     if (System.currentTimeMillis() - lastClear > clearInterval) {       clear();       return true;     }     return false;   }   }