Mybatis 缓存系统源码解析

本文从如下几个方面介绍:

相关文章sql

前言数据库

缓存的相关接口数组

一级缓存的实现过程缓存

二级缓存的实现过程安全

如何保证缓存的线程安全mybatis

缓存的装饰器app

相关文章

Mybatis 解析 SQL 源码分析二框架

Mybatis Mapper.xml 配置文件中 resultMap 节点的源码解析ide

Mybatis 解析 SQL 源码分析一源码分析

Mybatis Mapper 接口源码解析(binding包)

Mybatis 数据源和数据库链接池源码解析(DataSource)

Mybatis 类型转换源码分析

Mybatis 解析配置文件的源码解析

前言

在使用诸如 Mybatis 这种 ORM 框架的时候,通常都会提供缓存功能,用来缓存从数据库查询到的结果,当下一次查询条件相同的时候,只需从缓存中进行查找返回便可,若是缓存中没有,再去查库;一方面是提升查询速度,另外一方面是减小数据库压力;Mybatis 也提供了缓存,它分为一级缓存和二级缓存,接下来就来看看它的缓存系统是如何实现的。

缓存系统的实现使用了  模板方法模式 和 装饰器模式

接下来先来看下和缓存相关的接口

Cache

Mybatis 使用 Cache 来表示缓存,它是一个接口,定义了缓存须要的一些方法,以下所示:

public interface Cache {
  //获取缓存的id,即 namespace
  String getId();
  // 添加缓存
  void putObject(Object key, Object value);
  //根据key来获取缓存对应的值
  Object getObject(Object key);
  // 删除key对应的缓存
  Object removeObject(Object key);
  // 清空缓存  
  void clear();
  // 获取缓存中数据的大小
  int getSize();
  //取得读写锁, 从3.2.6开始没用了
  ReadWriteLock getReadWriteLock();
}

对于每个 namespace 都会建立一个缓存的实例,Cache 实现类的构造方法都必须传入一个 String 类型的ID,Mybatis自身的实现类都使用 namespace 做为 ID

PerpetualCache

Mybatis 为 Cache 接口提供的惟一一个实现类就是 PerpetualCache,这个惟一并非说 Cache 只有一个实现类,只是缓存的处理逻辑,Cache 还有其余的实现类,可是只是做为装饰器存在,只是对 Cache 进行包装而已。

PerpetualCache 的实现比较简单,就是把对应的 key-value 缓存数据存入到 map 中,以下所示:

public class PerpetualCache implements Cache {
  // id,通常对应mapper.xml 的namespace 的值
  private String id;
  
  // 用来存放数据,即缓存底层就是使用 map 来实现的
  private Map<Object, Object> cache = new HashMap<Object, Object>();

  public PerpetualCache(String id) {
    this.id = id;
  }
  //......其余的getter方法.....
  // 添加缓存
  @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();
  }
}

从上面的代码逻辑能够看到,mybatis 提供的缓存底层就是使用一个 HashMap 来实现的,可是咱们知道,HashMap 不是线程安全的,它是如何来保证缓存中的线程安全问题呢?在后面讲到 Cache 的包装类就知道,它提供了一个 SynchronizedCache 的装饰器类,就是用来包装线程安全的,在该类中全部方法都加上了 synchronized 关键字。

CacheKey

Mybatis 的缓存使用了 key-value 的形式存入到 HashMap 中,而 key 的话,Mybatis 使用了 CacheKey 来表示 key,它的生成规则为:mappedStementId + offset + limit + SQL + queryParams + environment生成一个哈希码.

public class CacheKey implements Cloneable, Serializable {

  private static final int DEFAULT_MULTIPLYER = 37;
  private static final int DEFAULT_HASHCODE = 17;

  // 参与计算hashcode,默认值为37
  private int multiplier;
  // CacheKey 对象的 hashcode ,默认值 17
  private int hashcode;
  // 检验和 
  private long checksum;
  // updateList 集合的个数
  private int count;
  // 由该集合中的全部对象来共同决定两个 CacheKey 是否相等
  private List<Object> updateList;

  public int getUpdateCount() {
    return updateList.size();
  }
  // 调用该方法,向 updateList 集合添加对应的对象
  public void update(Object object) {
    if (object != null && object.getClass().isArray()) {
      // 若是是数组,则循环处理每一项
      int length = Array.getLength(object);
      for (int i = 0; i < length; i++) {
        Object element = Array.get(object, i);
        doUpdate(element);
      }
    } else {
      doUpdate(object);
    }
  }
  // 计算 count checksum hashcode 和把对象添加到 updateList 集合中
  private void doUpdate(Object object) {
    int baseHashCode = object == null ? 1 : object.hashCode();
    count++;
    checksum += baseHashCode;
    baseHashCode *= count;
    hashcode = multiplier * hashcode + baseHashCode;

    updateList.add(object);
  }
 
  // 判断两个 CacheKey 是否相等
  @Override
  public boolean equals(Object object) {
    if (this == object) {
      return true;
    }
    if (!(object instanceof CacheKey)) {
      return false;
    }

    final CacheKey cacheKey = (CacheKey) object;

    if (hashcode != cacheKey.hashcode) {
      return false;
    }
    if (checksum != cacheKey.checksum) {
      return false;
    }
    if (count != cacheKey.count) {
      return false;
    }
    // 若是前几项都不知足,则循环遍历 updateList 集合,判断每一项是否相等,若是有一项不相等则这两个CacheKey不相等
    for (int i = 0; i < updateList.size(); i++) {
      Object thisObject = updateList.get(i);
      Object thatObject = cacheKey.updateList.get(i);
      if (thisObject == null) {
        if (thatObject != null) {
          return false;
        }
      } else {
        if (!thisObject.equals(thatObject)) {
          return false;
        }
      }
    }
    return true;
  }

  @Override
  public int hashCode() {
    return hashcode;
  }
}

若是须要进行缓存,则如何建立 CacheKey 呢?下面这个就是建立 一个 CacheKey 的方法:

public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    //cacheKey 对象 
    CacheKey cacheKey = new CacheKey();
    // 向 updateList 存入id
    cacheKey.update(ms.getId());
    // 存入offset
    cacheKey.update(rowBounds.getOffset());
    // 存入limit
    cacheKey.update(rowBounds.getLimit());
    // 存入sql
    cacheKey.update(boundSql.getSql());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
    for (ParameterMapping parameterMapping : parameterMappings) {
      if (parameterMapping.getMode() != ParameterMode.OUT) {
          String propertyName = parameterMapping.getProperty();
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          Object  value = metaObject.getValue(propertyName);
          // 存入每个参数
          cacheKey.update(value);
      }
    }
    if (configuration.getEnvironment() != null) {
      // 存入 environmentId
      cacheKey.update(configuration.getEnvironment().getId());
    }
    return cacheKey;
  }

从上面 CacheKey 和建立 CacheKey 的代码逻辑能够看出,Mybatis 的缓存使用了 mappedStementId + offset + limit + SQL + queryParams + environment 生成的hashcode做为 key。

了解了上述和缓存相关的接口后,接下来就来看看 Mybatis 的缓存系统是如何实现的,Mybatis 的缓存分为一级缓存和二级缓存,一级缓存是在 BaseExecutor 中实现的,二级缓存是在 CachingExecutor 中实现的。

Executor

Executor 接口定义了操做数据库的基本方法,SqlSession 的相关方法就是基于 Executor 接口实现的,它定义了操做数据库的方法以下:

public interface Executor {

  ResultHandler NO_RESULT_HANDLER = null;

  // insert | update | delete 的操做方法
  int update(MappedStatement ms, Object parameter) throws SQLException;
 
  // 查询,带分页,带缓存  
  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;

  // 查询,带分页 
  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

  // 查询存储过程
  <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;

  //刷新批处理语句
  List<BatchResult> flushStatements() throws SQLException;

  // 事务提交
  void commit(boolean required) throws SQLException;
  // 事务回滚
  void rollback(boolean required) throws SQLException;

  // 建立缓存的key
  CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
  // 是否缓存
  boolean isCached(MappedStatement ms, CacheKey key);
  // 清空缓存
  void clearLocalCache();
  // 延迟加载
  void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
  // 获取事务
  Transaction getTransaction();
}

一级缓存

BaseExecutor

BaseExecutor 是一个抽象类,实现了 Executor 接口,并提供了大部分方法的实现,只有 4 个基本方法:doUpdate,  doQuery,  doQueryCursor,  doFlushStatement 没有实现,仍是一个抽象方法,由子类实现,这 4 个方法至关于模板方法中变化的那部分。

Mybatis 的一级缓存就是在该类中实现的。

Mybatis 的一级缓存是会话级别的缓存,Mybatis 每建立一个 SqlSession 对象,就表示打开一次数据库会话,在一次会话中,应用程序极可能在短期内反复执行相同的查询语句,若是不对数据进行缓存,则每查询一次就要执行一次数据库查询,这就形成数据库资源的浪费。又由于经过 SqlSession 执行的操做,实际上由 Executor 来完成数据库操做的,因此在 Executor 中会创建一个简单的缓存,即一级缓存;将每次的查询结果缓存起来,再次执行查询的时候,会先查询一级缓存,若是命中,则直接返回,不然再去查询数据库并放入缓存中。

一级缓存的生命周期与 SqlSession 的生命周期相同,当调用 Executor.close 方法的时候,缓存变得不可用。一级缓存是默认开启的,通常状况下不须要特殊的配置,若是须要特殊配置,则能够经过插件的形式来实现

public abstract class BaseExecutor implements Executor {
  // 事务,提交,回滚,关闭事务
  protected Transaction transaction;
  // 底层的 Executor 对象
  protected Executor wrapper;
  // 延迟加载队列
  protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
  // 一级缓存,用于缓存查询结果
  protected PerpetualCache localCache;
  // 一级缓存,用于缓存输出类型参数(存储过程)
  protected PerpetualCache localOutputParameterCache;
  protected Configuration configuration;
  // 用来记录嵌套查询的层数
  protected int queryStack;
  private boolean closed;

  protected BaseExecutor(Configuration configuration, Transaction transaction) {
    this.transaction = transaction;
    this.deferredLoads = new ConcurrentLinkedQueue<DeferredLoad>();
    this.localCache = new PerpetualCache("LocalCache");
    this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
    this.closed = false;
    this.configuration = configuration;
    this.wrapper = this;
  }

// 4 个抽象方法,由子类实现,模板方法中可变部分
  protected abstract int doUpdate(MappedStatement ms, Object parameter)throws SQLException;
  protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException;
  protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)throws SQLException;
  protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)throws SQLException;

  // 执行 insert | update | delete 语句,调用 doUpdate 方法实现,在执行这些语句的时候,会清空缓存
  public int update(MappedStatement ms, Object parameter) throws SQLException {
    // ....
    // 清空缓存
    clearLocalCache();
    // 执行SQL语句
    return doUpdate(ms, parameter);
  }

  // 刷新批处理语句,且执行缓存中还没执行的SQL语句
  @Override
  public List<BatchResult> flushStatements() throws SQLException {
    return flushStatements(false);
  }
  public List<BatchResult> flushStatements(boolean isRollBack) throws SQLException {
    // ...
    // doFlushStatements 的 isRollBack 参数表示是否执行缓存中的SQL语句,false表示执行,true表示不执行
    return doFlushStatements(isRollBack);
  }
  
  // 查询存储过程
  @Override
  public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    return doQueryCursor(ms, parameter, rowBounds, boundSql);
  }

  // 事务的提交和回滚
  @Override
  public void commit(boolean required) throws SQLException {
    // 清空缓存
    clearLocalCache();
    // 刷新批处理语句,且执行缓存中的QL语句
    flushStatements();
    if (required) {
      transaction.commit();
    }
  }
  @Override
  public void rollback(boolean required) throws SQLException {
    if (!closed) {
      try {
        // 清空缓存
        clearLocalCache();
        // 刷新批处理语句,且不执行缓存中的SQL
        flushStatements(true);
      } finally {
        if (required) {
          transaction.rollback();
        }
      }
    }
  }

在上面的代码逻辑中,执行update类型的语句会清空缓存,且执行结果不须要进行缓存,而在执行查询语句的时候,须要对数据进行缓存,以下所示:

@Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    // 获取查询SQL
    BoundSql boundSql = ms.getBoundSql(parameter);
    // 建立缓存的key,建立逻辑在 CacheKey中已经分析过了
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    // 执行查询
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }

  // 执行查询逻辑
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    // ....
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      // 若是不是嵌套查询,且 <select> 的 flushCache=true 时才会清空缓存
      clearLocalCache();
    }
    List<E> list;
    try {
      // 嵌套查询层数加1
      queryStack++;
      // 首先从一级缓存中进行查询
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        // 若是命中缓存,则处理存储过程
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        // 若是缓存中没有对应的数据,则查数据库中查询数据
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    // ... 处理延迟加载的相关逻辑
    return list;
  }

  // 从数据库查询数据
  private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    // 在缓存中添加占位符
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      // 查库操做,由子类实现
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      // 删除占位符
      localCache.removeObject(key);
    }
    // 将从数据库查询的结果添加到一级缓存中
    localCache.putObject(key, list);
    // 处理存储过程
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

二级缓存

Mybatis 提供的二级缓存是应用级别的缓存,它的生命周期和应用程序的生命周期相同,且与二级缓存相关的配置有如下 3 个:

1. mybatis-config.xml 配置文件中的 cacheEnabled 配置,它是二级缓存的总开关,只有该配置为 true ,后面的缓存配置才会生效。默认为 true,即二级缓存默认是开启的。

2. Mapper.xml 配置文件中配置的 <cache> 和 <cache-ref>标签,若是 Mapper.xml 配置文件中配置了这两个标签中的任何一个,则表示开启了二级缓存的功能,在 Mybatis 解析 SQL 源码分析一 文章中已经分析过,若是配置了 <cache> 标签,则在解析配置文件的时候,会为该配置文件指定的 namespace 建立相应的 Cache 对象做为其二级缓存(默认为 PerpetualCache 对象),若是配置了 <cache-ref> 节点,则经过 ref 属性的namespace值引用别的Cache对象做为其二级缓存。经过 <cache> 和 <cache-ref> 标签来管理其在namespace中二级缓存功能的开启和关闭

3. <select> 节点中的 useCache 属性也能够开启二级缓存,该属性表示查询的结果是否要存入到二级缓存中,该属性默认为 true,也就是说 <select> 标签默认会把查询结果放入到二级缓存中。

 

 

Mybatis 的二级缓存是用 CachingExecutor 来实现的,它是 Executor 的一个装饰器类。为 Executor 对象添加了缓存的功能。

在介绍 CachingExecutor 以前,先来看看 CachingExecutor 依赖的两个类,TransactionalCacheManager 和 TransactionalCache。

TransactionalCache

TransactionalCache 实现了 Cache 接口,主要用于保存在某个 SqlSession 的某个事务中须要向某个二级缓存中添加的数据,代码以下:

public class TransactionalCache implements Cache {
  // 底层封装的二级缓存对应的Cache对象
  private Cache delegate;
  // 为true时,表示当前的 TransactionalCache 不可查询,且提交事务时会清空缓存
  private boolean clearOnCommit;
  // 存放须要添加到二级缓存中的数据
  private Map<Object, Object> entriesToAddOnCommit;
  // 存放为命中缓存的 CacheKey 对象
  private Set<Object> entriesMissedInCache;

  public TransactionalCache(Cache delegate) {
    this.delegate = delegate;
    this.clearOnCommit = false;
    this.entriesToAddOnCommit = new HashMap<Object, Object>();
    this.entriesMissedInCache = new HashSet<Object>();
  }

  // 添加缓存数据的时候,先暂时放到 entriesToAddOnCommit 集合中,在事务提交的时候,再把数据放入到二级缓存中,避免脏数据
  @Override
  public void putObject(Object key, Object object) {
    entriesToAddOnCommit.put(key, object);
  }
  // 提交事务,
  public void commit() {
    if (clearOnCommit) {
      delegate.clear();
    }
    // 把 entriesToAddOnCommit  集合中的数据放入到二级缓存中
    flushPendingEntries();
    reset();
  }
 // 把 entriesToAddOnCommit  集合中的数据放入到二级缓存中
  private void flushPendingEntries() {
    for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
      // 放入到二级缓存中
      delegate.putObject(entry.getKey(), entry.getValue());
    }
    for (Object entry : entriesMissedInCache) {
      if (!entriesToAddOnCommit.containsKey(entry)) {
        delegate.putObject(entry, null);
      }
    }
  }
 // 事务回滚
 public void rollback() {
    // 把未命中缓存的数据清除掉
    unlockMissedEntries();
    reset();
  }
  private void unlockMissedEntries() {
    for (Object entry : entriesMissedInCache) {
        delegate.removeObject(entry);
    }
  }

TransactionalCacheManager

TransactionalCacheManager 用于管理 CachingExecutor 使用的二级缓存:

public class TransactionalCacheManager {
 
  //用来管理 CachingExecutor 使用的二级缓存
  // key 为对应的CachingExecutor 使用的二级缓存
  // value 为对应的 TransactionalCache 对象
  private Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>();
  
  public void clear(Cache cache) {
    getTransactionalCache(cache).clear();
  }
  public Object getObject(Cache cache, CacheKey key) {
    return getTransactionalCache(cache).getObject(key);
  }  
  public void putObject(Cache cache, CacheKey key, Object value) {
    getTransactionalCache(cache).putObject(key, value);
  }
  public void commit() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.commit();
    }
  }
  public void rollback() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.rollback();
    }
  }
  // 全部的调用都会调用 TransactionalCache 的方法来实现
  private TransactionalCache getTransactionalCache(Cache cache) {
    TransactionalCache txCache = transactionalCaches.get(cache);
    if (txCache == null) {
      txCache = new TransactionalCache(cache);
      transactionalCaches.put(cache, txCache);
    }
    return txCache;
  }

}

CachingExecutor

接下来看下 二级缓存的实现 CachingExecutor :

public class CachingExecutor implements Executor {
  // 底层的 Executor
  private Executor delegate;
  private TransactionalCacheManager tcm = new TransactionalCacheManager();

  // 查询方法
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    // 获取 SQL
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    // 建立缓存key,在CacheKey中已经分析过建立过程
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
  
  // 查询
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    // 获取查询语句所在namespace对应的二级缓存
    Cache cache = ms.getCache();
    // 是否开启了二级缓存
    if (cache != null) {
      // 根据 <select> 的属性 useCache 的配置,决定是否须要清空二级缓存
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        // 二级缓存不能保存输出参数,不然抛异常
        ensureNoOutParams(ms, parameterObject, boundSql);
        // 从二级缓存中查询对应的值
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          // 若是二级缓存没有命中,则调用底层的 Executor 查询,其中会先查询一级缓存,一级缓存也未命中,才会去查询数据库
          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          // 查询到的数据放入到二级缓存中去
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    // 若是没有开启二级缓存,则直接调用底层的 Executor 查询,仍是会先查一级缓存
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

以上就是 Mybatis 的二级缓存的主要实现过程,CachingExecutor , TransactionalCacheManager 和 TransactionalCache 的关系以下所示,主要是经过 TransactionalCache 来操做二级缓存的。

此外,CachingExecutor 还有其余的一些方法,主要是调用底层封装的 Executor 来实现的。

以上就是 Mybatis 的一级缓存和二级缓存的实现过程。

Cache 装饰器

在介绍 Cache 接口的时候,说到,Cache 接口由不少的装饰器类,共 10 个,添加了不一样的功能,以下所示:

来看看 SynchronizedCache 装饰器类吧,在上面的缓存实现中介绍到了 Mybatis 其实就是使用 HashMap 来实现缓存的,即把数据放入到 HashMap中,可是 HashMap 不是线安全的,Mybatis 是如何来保证缓存中的线程安全问题呢?就是使用了 SynchronizedCache 来保证的,它是一个装饰器类,其中的方法都加上了 synchronized 关键字:

public class SynchronizedCache implements Cache {

  private Cache delegate;
  
  public SynchronizedCache(Cache delegate) {
    this.delegate = delegate;
  }
  @Override
  public synchronized int getSize() {
    return delegate.getSize();
  }
  @Override
  public synchronized void putObject(Object key, Object object) {
    delegate.putObject(key, object);
  }
  @Override
  public synchronized Object getObject(Object key) {
    return delegate.getObject(key);
  }

  @Override
  public synchronized Object removeObject(Object key) {
    return delegate.removeObject(key);
  }
  // ............
}

接下来看下添加 Cache 装饰器的方法,在 CacheBuilder.build() 方法中进行添加:

public class CacheBuilder {
  //...........
  // 建立缓存
  public Cache build() {
    // 设置缓存的实现类
    setDefaultImplementations();
    Cache cache = newBaseCacheInstance(implementation, id);
    setCacheProperties(cache);
    // 添加装饰器类
    if (PerpetualCache.class.equals(cache.getClass())) {
      for (Class<? extends Cache> decorator : decorators) {
        cache = newCacheDecoratorInstance(decorator, cache);
        setCacheProperties(cache);
      }
      // 为 Cache 添加装饰器
      cache = setStandardDecorators(cache);
    } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
      cache = new LoggingCache(cache);
    }
    return cache;
  }
  // 设置 Cache 的默认实现类为 PerpetualCache
  private void setDefaultImplementations() {
    if (implementation == null) {
      implementation = PerpetualCache.class;
      if (decorators.isEmpty()) {
        decorators.add(LruCache.class);
      }
    }
  }
  // 添加装饰器
  private Cache setStandardDecorators(Cache cache) {
    try {
      // 添加 ScheduledCache 装饰器
      if (clearInterval != null) {
        cache = new ScheduledCache(cache);
        ((ScheduledCache) cache).setClearInterval(clearInterval);
      }
      // 添加SerializedCache装饰器
      if (readWrite) {
        cache = new SerializedCache(cache);
      }
      // 添加 LoggingCache 装饰器
      cache = new LoggingCache(cache);
      // 添加  SynchronizedCache 装饰器,保证线程安全
      cache = new SynchronizedCache(cache);
      if (blocking) {
        // 添加 BlockingCache 装饰器
        cache = new BlockingCache(cache);
      }
      return cache;
  }
}

还有其余的装饰器,这里就不一一列出来了。

到这里 Mybatis 的缓存系统模块就分析完毕了。

相关文章
相关标签/搜索