• MyBatis源码分析(3)—— Cache接口以及实现


    @(MyBatis)[Cache]

    MyBatis源码分析——Cache接口以及实现

    Cache接口

    MyBatis中的Cache以SPI实现,给需要集成其它Cache或者自定义Cache提供了接口。

    public interface Cache {	
      String getId();
      void putObject(Object key, Object value);
      Object getObject(Object key);
      Object removeObject(Object key);
      void clear();
      int getSize();
      ReadWriteLock getReadWriteLock();
    }
    

    Cache实现

    Cache的实现类中,Cache有不同的功能,每个功能独立,互不影响,则对于不同的Cache功能,这里使用了装饰者模式实现。

    PerpetualCache

    作为为最基础的缓存类,底层实现比较简单,直接使用了HashMap。

    FifoCache

    FIFO回收策略,装饰类,内部维护了一个队列,来保证FIFO,一旦超出指定的大小,则从队列中获取Key并从被包装的Cache中移除该键值对。

    public class FifoCache implements Cache {
    
      // 被包装的类
      private final Cache delegate;
      // 队列,用来维持FIFO
      private LinkedList<Object> keyList;
      // 最大可容纳的大小
      private int size;
    
      public FifoCache(Cache delegate) {
        this.delegate = delegate;
        this.keyList = new LinkedList<Object>();
        this.size = 1024;
      }
    
      @Override
      public void putObject(Object key, Object value) {
        // 将Key放入队列中,并且检查一遍,如果满了则移除队列头部的元素
        cycleKeyList(key);
        // 执行真正的操作
        delegate.putObject(key, value);
      }
    
      private void cycleKeyList(Object key) {
        // 将Key放入队列
        keyList.addLast(key);
        if (keyList.size() > size) {
          // 超出指定容量,移除队列头部Key
          Object oldestKey = keyList.removeFirst();
          // 从缓存中移除Key对应的值
          delegate.removeObject(oldestKey);
        }
      }
      
      // 省略部分代码
      ...
    }
    

    LoggingCache

    日志功能,装饰类,用于记录缓存的命中率,如果开启了DEBUG模式,则会输出命中率日志。

    public class LoggingCache implements Cache {
    
      private Log log;  
      private Cache delegate;
      // 请求次数
      protected int requests = 0;
      // 命中次数
      protected int hits = 0;
    
      public LoggingCache(Cache delegate) {
        this.delegate = delegate;
        this.log = LogFactory.getLog(getId());
      }
    
      @Override
      public Object getObject(Object key) {
        requests++;
        final Object value = delegate.getObject(key);
        if (value != null) {
          // 命中
          hits++;
        }
        if (log.isDebugEnabled()) {
          // 如果开启了DEBUG模式,则输出命中率
          log.debug("Cache Hit Ratio [" + getId() + "]: " + getHitRatio());
        }
        return value;
      }
    
      // 获取命中率
      private double getHitRatio() {
        return (double) hits / (double) requests;
      }
    
      // 省略部分代码
      ...
    }
    

    LruCache

    LRU回收策略,装饰类,在内部保存一个LinkedHashMap,用以实现LRU。

    public class LruCache implements Cache {
    
      private final Cache delegate;
      private Map<Object, Object> keyMap;
      private Object eldestKey;
    
      public LruCache(Cache delegate) {
        this.delegate = delegate;
        // 初始化设置LRU回收的边界容量
        setSize(1024);
      }
    
      public void setSize(final int size) {
        keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
          private static final long serialVersionUID = 4267176411845948333L;
    
          // 键值移除策略,当大于指定容量时则移除最近最少使用的key/value
          protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
            boolean tooBig = size() > size;
            if (tooBig) {
              // 保存需要移除的键,因为在被包装的类中并不知道什么键需要移除
              eldestKey = eldest.getKey();
            }
            return tooBig;
          }
        };
      }
    
      @Override
      public void putObject(Object key, Object value) {
        delegate.putObject(key, value);
        // 将当前Key放到LRU的Map中,如果大于指定容量,则移除筛选的键值对
        cycleKeyList(key);
      }
    
      @Override
      public Object getObject(Object key) {
        // 让当前LRU的Map知道使用过
        keyMap.get(key); //touch
        return delegate.getObject(key);
      }
    
      @Override
      public Object removeObject(Object key) {
        // 这里没有移除当前维护的key,不过在后续也会被回收,可以忽略
        return delegate.removeObject(key);
      }
    
      private void cycleKeyList(Object key) {
        keyMap.put(key, key);
        if (eldestKey != null) {
          // 从Cache中移除掉LRU筛选出的键值对
          delegate.removeObject(eldestKey);
          eldestKey = null;
        }
      }
      // 省略部分代码...
    }
    

    ScheduledCache

    定时清空Cache,但是并没有开始一个定时任务,而是在使用Cache的时候,才去检查时间是否到了。

    public class ScheduledCache implements Cache {
    
      private 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 Object getObject(Object key) {
        if (clearWhenStale()) {
          return null;
        } else {
          return delegate.getObject(key);
        }
      }
    
      private boolean clearWhenStale() {
        if (System.currentTimeMillis() - lastClear > clearInterval) {
          // 时间到了,清空
          clear();
          return true;
        }
        return false;
      }
      
      @Override
      public void clear() {
        // 更新清空时间
        lastClear = System.currentTimeMillis();
        delegate.clear();
      }
      // 省略部分代码
    }
    

    SynchronizedCache

    同步Cache,实现比较简单,直接使用synchronized修饰方法。

    public class SynchronizedCache implements Cache {
    
      private Cache delegate;
      
      public SynchronizedCache(Cache delegate) {
        this.delegate = delegate;
      }
      @Override
      public synchronized void putObject(Object key, Object object) {
        delegate.putObject(key, object);
      }
      // 省略部分代码
    }
    

    SoftCache

    软引用回收策略,软引用只有当内存不足时才会被垃圾收集器回收。这里的实现机制中,使用了一个链表来保证一定数量的值即使内存不足也不会被回收,但是没有保存在该链表的值则有可能会被回收。
    在WeakHashMap中,可以看到是将引用应用到Key的,当Key被回收后,则移除相关的Value。但是这里是将其应用到Value中,因为Key不能被回收,如果被移除的话,就会影响到整个体系,最底层的实现使用HashMap实现的,没有Key,就没有办法移除相关的值。反过来,值被回收了,将软引用对象放到队列中,可以根据Key调用removeObject移除该关联的键和软引用对象。

    public class SoftCache implements Cache {
      // 用于保存一定数量强引用的值
      private final LinkedList<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 void putObject(Object key, Object value) {
        // 移除被垃圾收集器回收的键值
        removeGarbageCollectedItems();
        // 将软件用作用到Value中
        delegate.putObject(key, new SoftEntry(key, value, queueOfGarbageCollectedEntries));
      }
    
      @Override
      public Object getObject(Object key) {
        Object result = null;
        @SuppressWarnings("unchecked")
        SoftReference<Object> softReference = (SoftReference<Object>) delegate.getObject(key);
        if (softReference != null) {
          result = softReference.get();
          if (result == null) {
            // 该值被垃圾收集器回收,移除掉该项
            delegate.removeObject(key);
          } else {
            // 这里以及下面的clear,想不通为什么要加hardLinksToAvoidGarbageCollection的同步?(在WeakCache中却没有加同步)
            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) {
          // 这里需要清空该队列,否则即使下面调用clear,其Map清空了,但是部分值保留有引用,垃圾收集器也不会回收,会造成短暂的内存泄漏。
          hardLinksToAvoidGarbageCollection.clear();
        }
        removeGarbageCollectedItems();
        delegate.clear();
      }
    
      private void removeGarbageCollectedItems() {
        SoftEntry sv;
        // 清空被垃圾收集器回收的value其相关联的键以及软引用
        while ((sv = (SoftEntry) queueOfGarbageCollectedEntries.poll()) != null) {
          delegate.removeObject(sv.key);
        }
      }
    
      // 这里软引用对象是用在value中的
      private static class SoftEntry extends SoftReference<Object> {
        // 保存与value相关联的Key,因为一旦被垃圾收集器回收,则此软引用对象会被放到关联的引用队列中,这样就可以根据Key,移除该键值对。
        private final Object key;
    
        private SoftEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
          super(value, garbageCollectionQueue);
          this.key = key;
        }
      }
      // 省略部分代码
    }
    

    WeakCache

    弱引用回收策略,弱引用的对象一旦被垃圾收集器发现,则会被回收,无论内存是否足够。这里的实现和上面的软引用类似,除了使用WeakReference替换掉SoftReference,其它基本一样。还有一点想不通的就是,为什么SoftCache加锁了,而这里没有加锁。

      public Object getObject(Object key) {
        Object result = null;
        @SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cache
        WeakReference<Object> weakReference = (WeakReference<Object>) delegate.getObject(key);
        if (weakReference != null) {
          result = weakReference.get();
          if (result == null) {
            delegate.removeObject(key);
          } else {
            // 软引用这里加锁了
            hardLinksToAvoidGarbageCollection.addFirst(result);
            if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {
              hardLinksToAvoidGarbageCollection.removeLast();
            }
          }
        }
        return result;
      }
    

    TransactionalCache

    事务缓存,在提交的时候,才真正的放到Cache中,或者回滚的时候清除掉,对Cache没有影响。

    public class TransactionalCache implements Cache {
    
      private Cache delegate;
      private boolean clearOnCommit;
      private Map<Object, AddEntry> entriesToAddOnCommit;
      private Map<Object, RemoveEntry> entriesToRemoveOnCommit;
    
      public TransactionalCache(Cache delegate) {
        this.delegate = delegate;
        this.clearOnCommit = false;
        this.entriesToAddOnCommit = new HashMap<Object, AddEntry>();
        this.entriesToRemoveOnCommit = new HashMap<Object, RemoveEntry>();
      }
    
      @Override
      public Object getObject(Object key) {
        if (clearOnCommit) return null; // issue #146
        return delegate.getObject(key);
      }
    
      @Override
      public void putObject(Object key, Object object) {
        // 移除当前事务中 待移除键值对操作
        entriesToRemoveOnCommit.remove(key);
        // 添加当前事务中 待增加到缓存键值对操作
        entriesToAddOnCommit.put(key, new AddEntry(delegate, key, object));
      }
    
      @Override
      public Object removeObject(Object key) {
        // 移除增加该键值对的操作
        entriesToAddOnCommit.remove(key);
        // 添加移除键值对操作
        entriesToRemoveOnCommit.put(key, new RemoveEntry(delegate, key));
        return delegate.getObject(key);
      }
    
      @Override
      public void clear() {
        reset();
        clearOnCommit = true;
      }
    
      public void commit() {
        if (clearOnCommit) {
          // 当提交事务时需要先清空,则清空缓存
          delegate.clear();
        } else {
          // 应用移除键值对操作
          for (RemoveEntry entry : entriesToRemoveOnCommit.values()) {
            entry.commit();
          }
        }
        // 应用添加键值对操作
        for (AddEntry entry : entriesToAddOnCommit.values()) {
          entry.commit();
        }
        reset();
      }
    
      public void rollback() {
        reset();
      }
    
      private void reset() {
        clearOnCommit = false;
        entriesToRemoveOnCommit.clear();
        entriesToAddOnCommit.clear();
      }
    
      private static class AddEntry {
        private Cache cache;
        private Object key;
        private Object value;
    
        public AddEntry(Cache cache, Object key, Object value) {
          this.cache = cache;
          this.key = key;
          this.value = value;
        }
    
        public void commit() {
          // 提交的时候,才真正放入缓存
          cache.putObject(key, value);
        }
      }
    
      private static class RemoveEntry {
        private Cache cache;
        private Object key;
    
        public RemoveEntry(Cache cache, Object key) {
          this.cache = cache;
          this.key = key;
        }
    
        public void commit() {
          // 提交的时候才真正从缓存中移除
          cache.removeObject(key);
        }
      }
    }
    

    SerializedCache

    序列化功能,将值序列化后存到缓存中。该功能用于缓存返回一份实例的Copy,用于保存线程安全。

    public class SerializedCache implements Cache {
      // 省略部分代码	
    
      @Override
      public void putObject(Object key, Object object) {
        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);
        // 不为空,则反序列化,生成一份Copy
        return object == null ? null : deserialize((byte[]) object);
      }
    
      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 {
          // 此方法只有在待序列化的类第一次序列化的时候才会被调用
          // 遍历所支持的ClassLoader,加载对应的Class
          return Resources.classForName(desc.getName());
        }
      }
    }
  • 相关阅读:
    ByteArrayOutputStream的用法
    Oracle字符函数(转换大小写,替换等)
    Linux 结束占用端口的程序
    堆是先进先出,栈是先进后出
    帮小黎解决问题C++巩固获得数字每个位置上的数
    负数的二进制表示方法
    为什么1Byte=8bit
    黎活明给程序员的忠告
    寻找最好的编程语言
    U盘启动时无USB-HDD选项的解决方案
  • 原文地址:https://www.cnblogs.com/jabnih/p/5705640.html
Copyright © 2020-2023  润新知