1. 程式人生 > 實用技巧 >裝飾器模式在mybatis-cache包中的應用

裝飾器模式在mybatis-cache包中的應用

1. 裝飾器模式簡介

裝飾器模式主要解決的是直接繼承下因功能的不斷橫向擴充套件導致子類膨脹的問題,就功能實現來說,使用裝飾器模式比直接繼承顯得更加靈活,同時不需要考慮子類的維護。

上圖為裝飾器模式的組成,可以看出,其主要包含如下部分:

  • Component 定義一個物件的介面,定義了該物件的職責,也是裝飾類和被裝飾類的基本型別
  • ConcreteComponent 是Component 藉口的具體實現類,為被裝飾類
  • Decorator 是裝飾類,繼承Component介面,包含 Component介面例項
  • ConcreteDecoratorA,ConcreteDecoratorB 是ConcreteComponent的派生類,擴充套件了Decorator的職能

2. Mybatis快取裝飾器

下圖即為mybatis的cache包下的裝飾器實現結構

2.1 Cache

Cache介面定義了快取的一系列操作:

2.2PerpetualCache

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實現的。

還有其他的很多裝飾類,可以結合原始碼自行分析。