装饰器模式在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实现的。

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

 

posted @ 2020-10-31 18:36  雪山上的蒲公英  阅读(42)  评论(0编辑  收藏
/* 返回顶部代码 */