• 装饰器模式在mybatis-cache包中的应用


    1. 装饰器模式简介

    装饰器模式主要解决的是直接继承下因功能的不断横向扩展导致子类膨胀的问题,就功能实现来说,使用装饰器模式比直接继承显得更加灵活,同时不需要考虑子类的维护。

    上图为装饰器模式的组成,可以看出,其主要包含如下部分:

    • Component 定义一个对象的接口,定义了该对象的职责,也是装饰类和被装饰类的基本类型
    • ConcreteComponent 是Component 借口的具体实现类,为被装饰类
    • Decorator 是装饰类,继承Component接口,包含 Component接口实例
    • ConcreteDecoratorA,ConcreteDecoratorB 是ConcreteComponent的派生类,扩展了Decorator的职能

     2. Mybatis缓存装饰器

    下图即为mybatis的cache包下的装饰器实现结构

     2.1 Cache

    Cache接口定义了缓存的一系列操作:

     2.2 PerpetualCache

    PerpetualCache内部使用了HashMap作为Cache的存储结构,源码如下:

    public class PerpetualCache implements Cache {
    
      private final String id;
    
      //使用 HashMap 实现缓存功能
      private Map<Object, Object> cache = new HashMap<>();
    
      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();
      }
    
    }

    2.3 Decorators

    org.apache.ibatis.cache.decorators即为装饰类的存放路径,通过实现装饰类对功能的扩展,实现了各种策略的缓存实现。可以观察下图来查看其扩展细节:

    结合源码,简要分析一下部分装饰类的实现:

    2.3.1 LruCache

    public class LruCache implements Cache {
    
      private final Cache delegate;
      private Map<Object, Object> keyMap;
      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) {
        //初始化keyMap,底层继承自LinkedHashMap,并重写其removeEldestEntry方法
        keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
          private static final long serialVersionUID = 4267176411845948333L;
    
          @Override
          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);
        cycleKeyList(key);
      }
    
      @Override
      public Object getObject(Object key) {
        //刷新key 在keyMap 中的位置
        keyMap.get(key); //touch
        //从被装饰类中获取相应的缓存项
        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);
        if (eldestKey != null) {
          //从被装饰类中移除相应缓存项
          delegate.removeObject(eldestKey);
          eldestKey = null;
        }
      }
    
    }

    LruCache为一种具有LRU策略的缓存实现类,其keyMap属性类型继承自LinkedHashMap,用于保持键值对的插入顺序。

    2.3.2 BlockingCache

    public class BlockingCache implements Cache {
    
      private long timeout;
      private final Cache delegate;
      private final ConcurrentHashMap<Object, ReentrantLock> locks;
    
      public BlockingCache(Cache delegate) {
        this.delegate = delegate;
        this.locks = new ConcurrentHashMap<>();
      }
    
      @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);
        //缓存命中,则释放锁
        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) {
        return locks.computeIfAbsent(key, k -> new ReentrantLock());
      }
    
      private void acquireLock(Object key) {
        //computeIfAbsent获取锁
        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.lock();
        }
      }
    
      private void releaseLock(Object key) {
        //获取当前 key 对应的锁
        ReentrantLock lock = locks.get(key);
        if (lock.isHeldByCurrentThread()) {
          //unlock
          lock.unlock();
        }
      }
    
      public long getTimeout() {
        return timeout;
      }
    
      public void setTimeout(long timeout) {
        this.timeout = timeout;
      }
    }

    BlockingCache实现阻塞特性,其底层是基于可重入锁ReentrantLock实现的。

    还有其他的很多装饰类,可以结合源码自行分析。

  • 相关阅读:
    KMP算法的Next数组详解(转)
    公开封尘已久的即时通讯源码(转)
    《C语言编写 学生成绩管理系统》
    随想录(从编程语言到库、框架、软件)
    Effective Objective-C 2.0 笔记三(Literal Syntax简写语法)
    Java Swing 探索(一)LayoutManager
    Codeforces Round #FF 446 C. DZY Loves Fibonacci Numbers
    ARM体系结构与编程
    div:给div加滚动栏 div的滚动栏设置
    DS18B20
  • 原文地址:https://www.cnblogs.com/zjfjava/p/13907004.html
Copyright © 2020-2023  润新知