mybatis的Cache源码 终于让我搞懂了装饰器模式Decorator

前言

在学习设计模式时,对装饰器模式的定义还模糊不清,每次想在开发时使用该模式,总是不知道如何入手,从而转为使用其他模式去解决问题。

不过,就在这几天阅读mybatis的源码,看到了关于Cache缓存相关的使用方法,也算是大致明白如何使用了。

Cache类在mybatis中使用比较频繁,它除了基本的实现类外,还派生了很多装饰器类。关于装饰器模式的运用实例主要在本地缓存也就是二级缓存部分。

本文思路:

1. 装饰器定义和结构图

2. Cache装饰器在二级缓存中的形式

3. 缓存中使用装饰器的实际用例


装饰器模式

参考:装饰模式

定义

装饰器模式:动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。根据翻译的不同,装饰模式也有人称之为“油漆工模式”,它是一种对象结构型模式。

装饰器模式是添加额外的职责,而非对原有功能进行加工处理,这一点要注意。

装饰器的结构

../_images/Decorator.jpg


Mybatis Cache源码

 mybatis缓存装饰器的源码位置

简述Cache功能

Cache类是mybatis用于存储、查询结果的本地缓存,PerpetualCache是其基本的实现类。

下面是他们的基本结构

 Cache源代码

public interface Cache {

  String getId();

  void putObject(Object key, Object value);

  Object getObject(Object key);

  Object removeObject(Object key);

  void clear();

  int getSize();

  default ReadWriteLock getReadWriteLock() {
    return null;
  }

}

Cache的基础子类PerpetualCache

其实就是封装了一个HashMap对象,所有的缓存操作都是基于该map对象。

public class PerpetualCache implements Cache {

  private final String id;

  private final Map<Object, Object> cache = new HashMap<>();
  
  // …………
}


Mybatis Cache装饰器类

Cache所有装饰器类

decorators中,都是Cache的装饰器类,同时也是Cache的实现类,相当于这些装饰器类有两个身份:

  • 原对象实现类
  • 原对象装饰器类

有着这两重身份,各个装饰器之间就可以相互装饰。

注:作为Cache实现类时,必须要包装一个最终实现缓存操作的类,比如PerpetualCache。

Cache装饰器的作用

每个装饰器都有各自的独特行为,如下:

  • BlockingCache:阻塞装饰器,当在缓存中找不到元素时,它在cache key上加锁以阻塞该get操作直到该元素进入缓存
  • FifoCache:缓存元素超过一定限制后按队列形式先进先出
  • LoggingCache:增加操作日志
  • LruCache:缓存元素超过一定限制后删除最近最少使用的对象
  • ScheduledCache:定时清理缓存
  • SerializedCache:缓存可序列化
  • SoftCache:软引用缓存(即将发生OOM时,清理缓存)
  • WeakCache:弱引用缓存(每次GC时,都会清理缓存)
  • SynchronizedCache:并发安全
  • TransactionalCache:二级缓存事务缓冲区(作用暂时不明)

前面的定义描述了装饰器的作用:封装对象并为原对象绑定新的行为

那么创建装饰器,就只需要两个步骤:

  • 封装原对象到新的特殊对象
  • 在特殊对象里添加新的行为

立足这两点来分析Cache的装饰器,会发现装饰器模式还是挺简单的。

Cache装饰器:FifoCache

前面说到,创建装饰器有两步:封装、添加行为。

以Cache的其中一个装饰器FifoCache为例:

  1. 通过构造器,将Cache的实现类进行封装。
  2. 为Cache添加行为:缓存元素超过一定限制后按队列形式先进先出
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<>();
    this.size = 1024; // 限制容量为1024
  }

  //…………

  @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();
  }
  // 超过size便删除队首元素
  private void cycleKeyList(Object key) {
    keyList.addLast(key);
    if (keyList.size() > size) {
      Object oldestKey = keyList.removeFirst();
      delegate.removeObject(oldestKey);
    }
  }

}

上面介绍了装饰器作用和具体的装饰器类,接下来会了解它在代码中的具体使用方法。

装饰器大显身手

接下来就到装饰器的使用部分,这一部分最能体现装饰器的特点。

不过这里有一点要注意,当各个装饰器层层包装时,要动态删除某个装饰器很困难,且各个装饰器的功能将会在很大程度上受到装饰顺序的影响。

CacheBuilder构建装饰器

Cache由CacheBuilder#bulid方法进行构建,在创建Cache时,mybatis添加了适当的装饰器为Cache添加特色行为。

该建造者的建造方法如下:

// CacheBuilder  
  public Cache build() {
    setDefaultImplementations(); // 1
    Cache cache = newBaseCacheInstance(implementation, id); // 2
    setCacheProperties(cache); // 3
    // issue #352, do not apply decorators to custom caches
    if (PerpetualCache.class.equals(cache.getClass())) {
      for (Class<? extends Cache> decorator : decorators) {
        cache = newCacheDecoratorInstance(decorator, cache); // 4
        setCacheProperties(cache);
      }
      cache = setStandardDecorators(cache); // 5
    } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
      cache = new LoggingCache(cache); // 6
    }
    return cache;
  }

这里改建造者的参数来源于注解类@CacheNamespace

简单解释一下参数含义,对使用方法有兴趣可以自行查阅。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface CacheNamespace {
  // 设置缓存底层实现类
  Class<? extends Cache> implementation() default PerpetualCache.class;
  // 设置数据清理装饰器
  Class<? extends Cache> eviction() default LruCache.class;
  // 清理缓存间隔
  long flushInterval() default 0;
  // 缓存最大值
  int size() default 1024;
  // 是否启用读写缓存装饰器
  boolean readWrite() default true;
  // 是否启用阻塞装饰器
  boolean blocking() default false;
  // 预设缓存
  Property[] properties() default {};

}

装饰器的使用过程:

1. 构造器在创建时,会默认将实现类设置为PerpetualCache,并在装饰器列表中加上LruCache

  private void setDefaultImplementations() {
    if (implementation == null) {
      implementation = PerpetualCache.class;
      if (decorators.isEmpty()) {
        decorators.add(LruCache.class);
      }
    }
  }

2.  实例化刚刚设置的PerpetualCache实现类

  private Cache newBaseCacheInstance(Class<? extends Cache> cacheClass, String id) {
    Constructor<? extends Cache> cacheConstructor = getBaseCacheConstructor(cacheClass);
    try {
      return cacheConstructor.newInstance(id);
    } catch (Exception e) {
      throw new CacheException("Could not instantiate cache implementation (" + cacheClass + "). Cause: " + e, e);
    }
  }

3. 设置参数,如id,cache等,需要注意这些参数必须要有get、set方法

  private void setCacheProperties(Cache cache) {
    if (properties != null) {
      MetaObject metaCache = SystemMetaObject.forObject(cache);
      for (Map.Entry<Object, Object> entry : properties.entrySet()) {
        String name = (String) entry.getKey();
        String value = (String) entry.getValue();
        if (metaCache.hasSetter(name)) {
          Class<?> type = metaCache.getSetterType(name);
          if (String.class == type) {
            metaCache.setValue(name, value);
          } else if (int.class == type
            // …………
    // 初始化
    if (InitializingObject.class.isAssignableFrom(cache.getClass())) {
      try {
        ((InitializingObject) cache).initialize();
      } catch (Exception e) {
        throw new CacheException("Failed cache initialization for '"
          + cache.getId() + "' on '" + cache.getClass().getName() + "'", e);
      }
    }
  }

4.  使用装饰器来包装cache

    if (PerpetualCache.class.equals(cache.getClass())) {
      for (Class<? extends Cache> decorator : decorators) {
        cache = newCacheDecoratorInstance(decorator, cache);
        setCacheProperties(cache);
      }
      cache = setStandardDecorators(cache);
    }

5. 根据@CacheNamespace注解设置的属性值,来设置cache的默认参数,并选择装饰器进行装饰,可以看到日志和同步装饰器都是必选项。

  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 = 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);
    }
  }

6. 在步骤3的操作中,是可以将cache指向一个新的Cache类的,因此需要确保这个新的类也会记录日志。

else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
      cache = new LoggingCache(cache);
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值