MyBatis缓存责任链的建立

MyBatis缓存设计使用了装饰模式和责任链模式。本文将分析MyBatis缓存对象的结构和创建过程。

使用MyBatis缓存,首先要在mapper.xml文件中配置<cache>节点,当前命名空间下就开启了缓存。开启缓存的第一步就是创建缓存的容器,可以使用自定义的缓存容器,MyBatis默认提供PerpetualCache为缓存容器。创建缓存容器的过程首先是解析XML中的<cache>节点。

  /**
   * 解析<cache>节点
   * @param context 节点对象
   * @throws Exception
   */
  private void cacheElement(XNode context) throws Exception {
      // 配置了<cache>节点  
      if (context != null) {
      // 基础缓存的类型,可以自定义,默认为PERPETUAL
      String type = context.getStringAttribute("type", "PERPETUAL");
      // 加载类,如果配置错误,这里将抛找不到类异常
      Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
      // 缓存回收算法,默认为最少使用回收LRU
      String eviction = context.getStringAttribute("eviction", "LRU");
      // 同样去加载算法类
      Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
      // 缓存自动刷新时间
      Long flushInterval = context.getLongAttribute("flushInterval");
      // 缓存大小
      Integer size = context.getIntAttribute("size");
      // 是否只读
      boolean readWrite = !context.getBooleanAttribute("readOnly", false);
      // 其他配置属性
      Properties props = context.getChildrenAsProperties();
      // 创建缓存
      builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, props);
    }
  }
创建的过程使用建造者模式,通过CacheBuilder来创建Cache对象。创建结束后,将Cache添加都configuration对象,这是全局配置对象,存在整个MyBatis的生命周期中。
  /**
   * 创建新缓存容器
   * @param typeClass 基础缓存容器
   * @param evictionClass 缓存回收算法
   * @param flushInterval 有效时间
   * @param size 容器大小
   * @param readWrite 是否只读
   * @param props 其他属性
   * @return 缓存容器
   */
  public Cache useNewCache(Class<? extends Cache> typeClass,
      Class<? extends Cache> evictionClass,
      Long flushInterval,
      Integer size,
      boolean readWrite,
      Properties props) {
	  // 如果为空设置默认值
    typeClass = valueOrDefault(typeClass, PerpetualCache.class);
    evictionClass = valueOrDefault(evictionClass, LruCache.class);
    // 各个属性传入容器建造者,调用build创建缓存容器
    Cache cache = new CacheBuilder(currentNamespace)
        .implementation(typeClass)
        .addDecorator(evictionClass)
        .clearInterval(flushInterval)
        .size(size)
        .readWrite(readWrite)
        .properties(props)
        .build();
    // 将缓存容器添加到全局配置对象的范围
    configuration.addCache(cache);
    // 将当前缓存容器到Mapper的创建助手
    currentCache = cache;
    return cache;
  }
<pre name="code" class="java">  /**
   * 创建缓存容器
   * @return 缓存容器
   */
  public Cache build() {
    // 设置基础缓存容器,回收算法的默认实现
    setDefaultImplementations();
    // implementation是一个Class对象,通过反射创建缓存容器对象,并设置id
    Cache cache = newBaseCacheInstance(implementation, id);
    setCacheProperties(cache);
    if (PerpetualCache.class.equals(cache.getClass())) { // issue #352, do not apply decorators to custom caches
      for (Class<? extends Cache> decorator : decorators) {
    	// 回收算法容器  
        cache = newCacheDecoratorInstance(decorator, cache);
        setCacheProperties(cache);
      }
        // 构造标准缓存链
<span>	</span>cache = setStandardDecorators(cache);
    } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
    	// 日志容器
    	cache = new LoggingCache(cache);
    }
    // 返回一个缓存容器。
    return cache;
  }
 

首先是通过反射创建基础的容器,有了基础容器的Class类型,通过反射取得构造参数为String的构造方法,然后将id传入,完成基础容器的构建。

  /**
   * 通过反射获得基础缓存容器对象
   * @param cacheClass 缓存类型
   * @param id 标识符
   * @return 基础缓存容器对象
   */
  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);
    }
  }

  /**
   * 获取构造函数
   * @param cacheClass 缓存类型
   * @return 缓存类型的构造函数
   */
  private Constructor<? extends Cache> getBaseCacheConstructor(Class<? extends Cache> cacheClass) {
    try {
      return cacheClass.getConstructor(String.class);
    } catch (Exception e) {
      throw new CacheException("Invalid base cache implementation (" + cacheClass + ").  " +
          "Base cache implementations must have a constructor that takes a String id as a parameter.  Cause: " + e, e);
    }
  }
这个基础的容器该容器使用Map<Object,Object>来放缓存,3.2.6版本之前有一把读写锁,客户端可以通过get方法获取该锁,然后对数据进行操作,之后的版本取消了读写锁。
回到builder方法,创建基础容器结束后,下一步要设置缓存回收算法cache = newCacheDecoratorInstance(decorator, cache),还是使用反射,获得构造方法参数为Class,Cache的构造方法,不仅需要算法的类型,而且构造函数里面还需要一个缓存容器对象Cache。看一下LruCache类的成员。

  // 代理对象	
  private final Cache delegate;
  // 用于实现lru算法的map
  private Map<Object, Object> keyMap;
  // 最近最少使用的键
  private Object eldestKey;

外部传进来的基础容器对象,在这里面成了被代理的对象,而且维护一个keyMap来实现lru算法。因为LruCache和PerpetualCache都实现了Cache接口,所以在使用LruCache的时候,调用的是被代理PerpetualCache的方法。

  @Override
  public String getId() {
    return delegate.getId();
  }

  @Override
  public int getSize() {
    return delegate.getSize();
  }
特殊的地方在于创建时要维护的keyMap,已经取缓存操作,放缓存操作时,要计算缓存使用的频率,然后清空较少使用的缓存。
  /**
   * 初始化时设置长度
   * @param size 默认1024
   */
  public void setSize(final int size) {
	// 使用LinkedHashMap来实现LRU算法
    keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
      private static final long serialVersionUID = 4267176411845948333L;
      // 重写removeEldestEntry方法
      protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
        // 缓存多于预定的数量
    	boolean tooBig = size() > size;
        if (tooBig) {
          // 缓存中最少使用的键	
          eldestKey = eldest.getKey();
        }
        return tooBig;
      }
    };
  }
通过get、set取缓存,并且维护缓存容器。

@Override
  public Object getObject(Object key) {
	// 执行一个get操作,记录key的使用  
    keyMap.get(key);
    // 通过代理从基础缓存容器中取出真正的缓存对象
    return delegate.getObject(key);
  }
  @Override
  public void putObject(Object key, Object value) {
	// 将对象放入基础缓存容器  
    delegate.putObject(key, value);
    // 维护lru算法map
    cycleKeyList(key);
  }
private void cycleKeyList(Object key) {
    // 放入key
    keyMap.put(key, key);
    // 如果存在要异常的对象,调用代理容器移除缓存
    if (eldestKey != null) {
      delegate.removeObject(eldestKey);
      eldestKey = null;
    }
  }

回到之前的cache = newCacheDecoratorInstance(decorator, cache);返回的Cache对象将基础缓存容器包装了一层,对lruCache操作时,维护lru算法,然后通过代理对象执行真正的取缓存、放缓存操作。

接下来build()方法中还执行了cache = setStandardDecorators(cache);接着对cache执行包装。

  /**
   * 设置标准装饰器
   * @param cache 缓存容器
   * @return 缓存容器
   */
  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);
      return cache;
    } catch (Exception e) {
      throw new CacheException("Error building standard cache decorators.  Cause: " + e, e);
    }
  }
通过一层层的装饰,形成了一条责任链,链上每个cache完成自己的功能,然后委托下一节点进行操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值