- 前言
- 缓存的相关接口
- 一级缓存的实现过程
- 二级缓存的实现过程
- 如何保证缓存的线程安全
- 缓存的装饰器
二级缓存的实现过程
前言
在使用诸如
Mybatis
这种 ORM 框架的时候,一般都会提供缓存功能,用来缓存从数据库查询到的结果,当下一次查询条件相同的时候,只需从缓存中进行查找返回即可,如果缓存中没有,再去查库;一方面是提高查询速度,另一方面是减少数据库压力;
Mybatis
也提供了缓存,它分为
一级缓存
和
二级缓存
,接下来就来看看它的缓存系统是如何实现的。
缓存系统的实现使用了
模板方法模式
**** 和
装饰器模式
接下来先来看下和缓存相关的接口
Cache
Mybatis
使用
Cache
来表示缓存,它是一个接口,定义了缓存需要的一些方法,如下所示:
1public interface Cache {
2 //获取缓存的id,即 namespace
3 String getId();
4 // 添加缓存
5 void putObject(Object key, Object value);
6 //根据key来获取缓存对应的值
7 Object getObject(Object key);
8 // 删除key对应的缓存
9 Object removeObject(Object key);
10 // 清空缓存
11 void clear();
12 // 获取缓存中数据的大小
13 int getSize();
14 //取得读写锁, 从3.2.6开始没用了
15 ReadWriteLock getReadWriteLock();
16}
对于每一个
namespace
都会创建一个缓存的实例,
Cache
实现类的构造方法都必须传入一个 String 类型的 ID,Mybatis自身的实现类都使用
namespace
作为 ID
PerpetualCache
Mybatis 为 Cache 接口提供的唯一一个实现类就是
PerpetualCache
,这个唯一并不是说 Cache 只有一个实现类,只是缓存的处理逻辑,Cache 还有其他的实现类,但是只是作为装饰器存在,只是对 Cache 进行包装而已。
PerpetualCache
的实现比较简单,就是把对应的
key-value
缓存数据存入到
map
中,如下所示:
1public class PerpetualCache implements Cache {
2 // id,一般对应mapper.xml 的namespace 的值
3 private String id;
4
5 // 用来存放数据,即缓存底层就是使用 map 来实现的
6 private MapObject, Object cache = new HashMapObject, Object();
7
8 public PerpetualCache(String id) {
9 this.id = id;
10 }
11 //......其他的getter方法.....
12 // 添加缓存
13 @Override
14 public void putObject(Object key, Object value) {
15 cache.put(key, value);
16 }
17 // 获取缓存
18 @Override
19 public Object getObject(Object key) {
20 return cache.get(key);
21 }
22 // 删除缓存
23 @Override
24 public Object removeObject(Object key) {
25 return cache.remove(key);
26 }
27 // 清空缓存
28 @Override
29 public void clear() {
30 cache.clear();
31 }
32}
从上面的代码逻辑可以看到,mybatis 提供的缓存底层就是使用一个
HashMap
来实现的,但是我们知道,
HashMap
不是线程安全的,它是如何来保证缓存中的线程安全问题呢? 在后面讲到
Cache
的包装类就知道,它提供了一个
SynchronizedCache
的装饰器类,就是用来包装线程安全的,在该类中所有方法都加上了
synchronized
关键字。
CacheKey
Mybatis 的缓存使用了 key-value 的形式存入到
HashMap
中,而
key
的话,Mybatis 使用了
CacheKey
来表示 key,它的生成规则为:
mappedStementId + offset + limit + SQL + queryParams + environment
生成一个哈希码.
1public class CacheKey implements Cloneable, Serializable {
2
3 private static final int DEFAULT_MULTIPLYER = 37;
4 private static final int DEFAULT_HASHCODE = 17;
5
6 // 参与计算hashcode,默认值为37
7 private int multiplier;
8 // CacheKey 对象的 hashcode ,默认值 17
9 private int hashcode;
10 // 检验和
11 private long checksum;
12 // updateList 集合的个数
13 private int count;
14 // 由该集合中的所有对象来共同决定两个 CacheKey 是否相等
15 private ListObject updateList;
16
17 public int getUpdateCount() {
18 return updateList.size();
19 }
20 // 调用该方法,向 updateList 集合添加对应的对象
21 public void update(Object object) {
22 if (object != null && object.getClass().isArray()) {
23 // 如果是数组,则循环处理每一项
24 int length = Array.getLength(object);
25 for (int i = 0; i length; i++) {
26 Object element = Array.get(object, i);
27 doUpdate(element);
28 }
29 } else {
30 doUpdate(object);
31 }
32 }
33 // 计算 count checksum hashcode 和把对象添加到 updateList 集合中
34 private void doUpdate(Object object) {
35 int baseHashCode = object == null ? 1 : object.hashCode();
36 count++;
37 checksum += baseHashCode;
38 baseHashCode *= count;
39 hashcode = multiplier * hashcode + baseHashCode;
40
41 updateList.add(object);
42 }
43
44 // 判断两个 CacheKey 是否相等
45 @Override
46 public boolean equals(Object object) {
47 if (this == object) {
48 return true;
49 }
50 if (!(object instanceof CacheKey)) {
51 return false;
52 }
53
54 final CacheKey cacheKey = (CacheKey) object;
55
56 if (hashcode != cacheKey.hashcode) {
57 return false;
58 }
59 if (checksum != cacheKey.checksum) {
60 return false;
61 }
62 if (count != cacheKey.count) {
63 return false;
64 }
65 // 如果前几项都不满足,则循环遍历 updateList 集合,判断每一项是否相等,如果有一项不相等则这两个CacheKey不相等
66 for (int i = 0; i updateList.size(); i++) {
67 Object thisObject = updateList.get(i);
68 Object thatObject = cacheKey.updateList.get(i);
69 if (thisObject == null) {
70 if (thatObject != null) {
71 return false;
72 }
73 } else {
74 if (!thisObject.equals(thatObject)) {
75 return false;
76 }
77 }
78 }
79 return true;
80 }
81
82 @Override
83 public int hashCode() {
84 return hashcode;
85 }
86}
如果需要进行缓存,则如何创建
CacheKey
呢?下面这个就是创建 一个
CacheKey
的方法:
1 public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
2 //cacheKey 对象
3 CacheKey cacheKey = new CacheKey();
4 // 向 updateList 存入id
5 cacheKey.update(ms.getId());
6 // 存入offset
7 cacheKey.update(rowBounds.getOffset());
8 // 存入limit
9 cacheKey.update(rowBounds.getLimit());
10 // 存入sql
11 cacheKey.update(boundSql.getSql());
12 ListParameterMapping parameterMappings = boundSql.getParameterMappings();
13 TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
14 for (ParameterMapping parameterMapping : parameterMappings) {
15 if (parameterMapping.getMode() != ParameterMode.OUT) {
16 String propertyName = parameterMapping.getProperty();
17 MetaObject metaObject = configuration.newMetaObject(parameterObject);
18 Object value = metaObject.getValue(propertyName);
19 // 存入每一个参数
20 cacheKey.update(value);
21 }
22 }
23 if (configuration.getEnvironment() != null) {
24 // 存入 environmentId
25 cacheKey.update(configuration.getEnvironment().getId());
26 }
27 return cacheKey;
28 }
从上面 CacheKey 和创建 CacheKey 的代码逻辑可以看出,Mybatis 的缓存使用了
mappedStementId + offset + limit + SQL + queryParams + environment 生成的hashcode
作为 key。
了解了上述和缓存相关的接口后,接下来就来看看 Mybatis 的缓存系统是如何实现的,Mybatis 的缓存分为
一级缓存
和
二级缓存
,
一级缓存
是在
BaseExecutor
中实现的,
二级缓存
是在
CachingExecutor
中实现的。
Executor
Executor 接口定义了操作数据库的基本方法,SqlSession 的相关方法就是基于 Executor 接口实现的,它定义了操作数据库的方法如下:
1public interface Executor {
2
3 ResultHandler NO_RESULT_HANDLER = null;
4
5 // insert | update | delete 的操作方法
6 int update(MappedStatement ms, Object parameter) throws SQLException;
7
8 // 查询,带分页,带缓存
9 E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
10
11 // 查询,带分页
12 E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
13
14 // 查询存储过程
15 E CursorE queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
16
17 //刷新批处理语句
18 ListBatchResult flushStatements() throws SQLException;
19
20 // 事务提交
21 void commit(boolean required) throws SQLException;
22 // 事务回滚
23 void rollback(boolean required) throws SQLException;
24
25 // 创建缓存的key
26 CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
27 // 是否缓存
28 boolean isCached(MappedStatement ms, CacheKey key);
29 // 清空缓存
30 void clearLocalCache();
31 // 延迟加载
32 void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class? targetType);
33 // 获取事务
34 Transaction getTransaction();
35}
一级缓存
BaseExecutor
BaseExecutor
是一个抽象类,实现了
Executor
接口,并提供了大部分方法的实现,只有 4 个基本方法:
doUpdate
,
doQuery
,
doQueryCursor
,
doFlushStatement
没有实现,还是一个抽象方法,由子类实现,这 4 个方法相当于模板方法中变化的那部分。
Mybatis 的一级缓存就是在该类中实现的。
Mybatis 的一级缓存是会话级别的缓存,Mybatis 每创建一个 SqlSession 对象,就表示打开一次数据库会话,在一次会话中,应用程序很可能在短时间内反复执行相同的查询语句,如果不对数据进行缓存,则每查询一次就要执行一次数据库查询,这就造成数据库资源的浪费。又因为通过 SqlSession 执行的操作,实际上由 Executor 来完成数据库操作的,所以在 Executor 中会建立一个简单的缓存,即一级缓存;将每次的查询结果缓存起来,再次执行查询的时候,会先查询一级缓存,如果命中,则直接返回,否则再去查询数据库并放入缓存中。
一级缓存的生命周期与 SqlSession 的生命周期相同,当调用 Executor.close 方法的时候,缓存变得不可用。一级缓存是默认开启的,一般情况下不需要特殊的配置,如果需要特殊配置,则可以通过插件的形式来实现
1public abstract class BaseExecutor implements Executor {
2 // 事务,提交,回滚,关闭事务
3 protected Transaction transaction;
4 // 底层的 Executor 对象
5 protected Executor wrapper;
6 // 延迟加载队列
7 protected ConcurrentLinkedQueueDeferredLoad deferredLoads;
8 // 一级缓存,用于缓存查询结果
9 protected PerpetualCache localCache;
10 // 一级缓存,用于缓存输出类型参数(存储过程)
11 protected PerpetualCache localOutputParameterCache;
12 protected Configuration configuration;
13 // 用来记录嵌套查询的层数
14 protected int queryStack;
15 private boolean closed;
16
17 protected BaseExecutor(Configuration configuration, Transaction transaction) {
18 this.transaction = transaction;
19 this.deferredLoads = new ConcurrentLinkedQueueDeferredLoad();
20 this.localCache = new PerpetualCache("LocalCache");
21 this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
22 this.closed = false;
23 this.configuration = configuration;
24 this.wrapper = this;
25 }
26
27// 4 个抽象方法,由子类实现,模板方法中可变部分
28 protected abstract int doUpdate(MappedStatement ms, Object parameter)throws SQLException;
29 protected abstract ListBatchResult doFlushStatements(boolean isRollback) throws SQLException;
30 protected abstract E ListE doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)throws SQLException;
31 protected abstract E CursorE doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)throws SQLException;
32
33 // 执行 insert | update | delete 语句,调用 doUpdate 方法实现,在执行这些语句的时候,会清空缓存
34 public int update(MappedStatement ms, Object parameter) throws SQLException {
35 // ....
36 // 清空缓存
37 clearLocalCache();
38 // 执行SQL语句
39 return doUpdate(ms, parameter);
40 }
41
42 // 刷新批处理语句,且执行缓存中还没执行的SQL语句
43 @Override
44 public ListBatchResult flushStatements() throws SQLException {
45 return flushStatements(false);
46 }
47 public ListBatchResult flushStatements(boolean isRollBack) throws SQLException {
48 // ...
49 // doFlushStatements 的 isRollBack 参数表示是否执行缓存中的SQL语句,false表示执行,true表示不执行
50 return doFlushStatements(isRollBack);
51 }
52
53 // 查询存储过程
54 @Override
55 public E CursorE queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
56 BoundSql boundSql = ms.getBoundSql(parameter);
57 return doQueryCursor(ms, parameter, rowBounds, boundSql);
58 }
59
60 // 事务的提交和回滚
61 @Override
62 public void commit(boolean required) throws SQLException {
63 // 清空缓存
64 clearLocalCache();
65 // 刷新批处理语句,且执行缓存中的QL语句
66 flushStatements();
67 if (required) {
68 transaction.commit();
69 }
70 }
71 @Override
72 public void rollback(boolean required) throws SQLException {
73 if (!closed) {
74 try {
75 // 清空缓存
76 clearLocalCache();
77 // 刷新批处理语句,且不执行缓存中的SQL
78 flushStatements(true);
79 } finally {
80 if (required) {
81 transaction.rollback();
82 }
83 }
84 }
85 }
在上面的代码逻辑中,执行
update
类型的语句会清空缓存,且执行结果不需要进行缓存,而在执行查询语句的时候,需要对数据进行缓存,如下所示:
1 @Override
2 public E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
3 // 获取查询SQL
4 BoundSql boundSql = ms.getBoundSql(parameter);
5 // 创建缓存的key,创建逻辑在 CacheKey中已经分析过了
6 CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
7 // 执行查询
8 return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
9 }
10
11 // 执行查询逻辑
12 public E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
13 // ....
14 if (queryStack == 0 && ms.isFlushCacheRequired()) {
15 // 如果不是嵌套查询,且 select 的 flushCache=true 时才会清空缓存
16 clearLocalCache();
17 }
18 ListE list;
19 try {
20 // 嵌套查询层数加1
21 queryStack++;
22 // 首先从一级缓存中进行查询
23 list = resultHandler == null ? (ListE) localCache.getObject(key) : null;
24 if (list != null) {
25 // 如果命中缓存,则处理存储过程
26 handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
27 } else {
28 // 如果缓存中没有对应的数据,则查数据库中查询数据
29 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
30 }
31 } finally {
32 queryStack--;
33 }
34 // ... 处理延迟加载的相关逻辑
35 return list;
36 }
37
38 // 从数据库查询数据
39 private E ListE queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
40 ListE list;
41 // 在缓存中添加占位符
42 localCache.putObject(key, EXECUTION_PLACEHOLDER);
43 try {
44 // 查库操作,由子类实现
45 list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
46 } finally {
47 // 删除占位符
48 localCache.removeObject(key);
49 }
50 // 将从数据库查询的结果添加到一级缓存中
51 localCache.putObject(key, list);
52 // 处理存储过程
53 if (ms.getStatementType() == StatementType.CALLABLE) {
54 localOutputParameterCache.putObject(key, parameter);
55 }
56 return list;
57 }
二级缓存
Mybatis 提供的二级缓存是应用级别的缓存,它的生命周期和应用程序的生命周期相同,且与二级缓存相关的配置有以下 3 个:
Mybatis 的二级缓存是用
CachingExecutor
来实现的,它是
Executor
的一个装饰器类。为
Executor
对象添加了缓存的功能。
在介绍
CachingExecutor
之前,先来看看
CachingExecutor
依赖的两个类,
TransactionalCacheManager
和
TransactionalCache
。
TransactionalCache
TransactionalCache
实现了 Cache 接口,主要用于保存在某个
SqlSession
的某个事务中需要向某个二级缓存中添加的数据,代码如下:
1public class TransactionalCache implements Cache {
2 // 底层封装的二级缓存对应的Cache对象
3 private Cache delegate;
4 // 为true时,表示当前的 TransactionalCache 不可查询,且提交事务时会清空缓存
5 private boolean clearOnCommit;
6 // 存放需要添加到二级缓存中的数据
7 private MapObject, Object entriesToAddOnCommit;
8 // 存放为命中缓存的 CacheKey 对象
9 private SetObject entriesMissedInCache;
10
11 public TransactionalCache(Cache delegate) {
12 this.delegate = delegate;
13 this.clearOnCommit = false;
14 this.entriesToAddOnCommit = new HashMapObject, Object();
15 this.entriesMissedInCache = new HashSetObject();
16 }
17
18 // 添加缓存数据的时候,先暂时放到 entriesToAddOnCommit 集合中,在事务提交的时候,再把数据放入到二级缓存中,避免脏数据
19 @Override
20 public void putObject(Object key, Object object) {
21 entriesToAddOnCommit.put(key, object);
22 }
23 // 提交事务,
24 public void commit() {
25 if (clearOnCommit) {
26 delegate.clear();
27 }
28 // 把 entriesToAddOnCommit 集合中的数据放入到二级缓存中
29 flushPendingEntries();
30 reset();
31 }
32 // 把 entriesToAddOnCommit 集合中的数据放入到二级缓存中
33 private void flushPendingEntries() {
34 for (Map.EntryObject, Object entry : entriesToAddOnCommit.entrySet()) {
35 // 放入到二级缓存中
36 delegate.putObject(entry.getKey(), entry.getValue());
37 }
38 for (Object entry : entriesMissedInCache) {
39 if (!entriesToAddOnCommit.containsKey(entry)) {
40 delegate.putObject(entry, null);
41 }
42 }
43 }
44 // 事务回滚
45 public void rollback() {
46 // 把未命中缓存的数据清除掉
47 unlockMissedEntries();
48 reset();
49 }
50 private void unlockMissedEntries() {
51 for (Object entry : entriesMissedInCache) {
52 delegate.removeObject(entry);
53 }
54 }
TransactionalCacheManager
TransactionalCacheManager
用于管理
CachingExecutor
使用的二级缓存:
1public class TransactionalCacheManager {
2
3 //用来管理 CachingExecutor 使用的二级缓存
4 // key 为对应的CachingExecutor 使用的二级缓存
5 // value 为对应的 TransactionalCache 对象
6 private MapCache, TransactionalCache transactionalCaches = new HashMapCache, TransactionalCache();
7
8 public void clear(Cache cache) {
9 getTransactionalCache(cache).clear();
10 }
11 public Object getObject(Cache cache, CacheKey key) {
12 return getTransactionalCache(cache).getObject(key);
13 }
14 public void putObject(Cache cache, CacheKey key, Object value) {
15 getTransactionalCache(cache).putObject(key, value);
16 }
17 public void commit() {
18 for (TransactionalCache txCache : transactionalCaches.values()) {
19 txCache.commit();
20 }
21 }
22 public void rollback() {
23 for (TransactionalCache txCache : transactionalCaches.values()) {
24 txCache.rollback();
25 }
26 }
27 // 所有的调用都会调用 TransactionalCache 的方法来实现
28 private TransactionalCache getTransactionalCache(Cache cache) {
29 TransactionalCache txCache = transactionalCaches.get(cache);
30 if (txCache == null) {
31 txCache = new TransactionalCache(cache);
32 transactionalCaches.put(cache, txCache);
33 }
34 return txCache;
35 }
36}
CachingExecutor
接下来看下 二级缓存的实现
CachingExecutor
:
1public class CachingExecutor implements Executor {
2 // 底层的 Executor
3 private Executor delegate;
4 private TransactionalCacheManager tcm = new TransactionalCacheManager();
5
6 // 查询方法
7 @Override
8 public E ListE query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
9 // 获取 SQL
10 BoundSql boundSql = ms.getBoundSql(parameterObject);
11 // 创建缓存key,在CacheKey中已经分析过创建过程
12 CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
13 return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
14 }
15
16 // 查询
17 public E ListE query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
18 throws SQLException {
19 // 获取查询语句所在namespace对应的二级缓存
20 Cache cache = ms.getCache();
21 // 是否开启了二级缓存
22 if (cache != null) {
23 // 根据 select 的属性 useCache 的配置,决定是否需要清空二级缓存
24 flushCacheIfRequired(ms);
25 if (ms.isUseCache() && resultHandler == null) {
26 // 二级缓存不能保存输出参数,否则抛异常
27 ensureNoOutParams(ms, parameterObject, boundSql);
28 // 从二级缓存中查询对应的值
29 ListE list = (ListE) tcm.getObject(cache, key);
30 if (list == null) {
31 // 如果二级缓存没有命中,则调用底层的 Executor 查询,其中会先查询一级缓存,一级缓存也未命中,才会去查询数据库
32 list = delegate.E query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
33 // 查询到的数据放入到二级缓存中去
34 tcm.putObject(cache, key, list); // issue #578 and #116
35 }
36 return list;
37 }
38 }
39 // 如果没有开启二级缓存,则直接调用底层的 Executor 查询,还是会先查一级缓存
40 return delegate.E query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
41 }
以上就是 Mybatis 的二级缓存的主要实现过程,
CachingExecutor
,
TransactionalCacheManager
和
TransactionalCache
的关系如下所示,主要是通过
TransactionalCache
来操作二级缓存的。
此外,
CachingExecutor
还有其他的一些方法,主要是调用底层封装的
Executor
来实现的。
以上就是 Mybatis 的一级缓存和二级缓存的实现过程。
Cache 装饰器
在介绍 Cache 接口的时候,说到,Cache 接口由很多的装饰器类,共 10 个,添加了不同的功能,如下所示:
来看看
SynchronizedCache
装饰器类吧,在上面的缓存实现中介绍到了 Mybatis 其实就是使用
HashMap
来实现缓存的,即把数据放入到
HashMap
中,但是
HashMap
不是线安全的,Mybatis 是如何来保证缓存中的线程安全问题呢?就是使用了
SynchronizedCache
来保证的,它是一个装饰器类,其中的方法都加上了
synchronized
关键字:
1public class SynchronizedCache implements Cache {
2
3 private Cache delegate;
4
5 public SynchronizedCache(Cache delegate) {
6 this.delegate = delegate;
7 }
8 @Override
9 public synchronized int getSize() {
10 return delegate.getSize();
11 }
12 @Override
13 public synchronized void putObject(Object key, Object object) {
14 delegate.putObject(key, object);
15 }
16 @Override
17 public synchronized Object getObject(Object key) {
18 return delegate.getObject(key);
19 }
20
21 @Override
22 public synchronized Object removeObject(Object key) {
23 return delegate.removeObject(key);
24 }
25 // ............
26}
接下来看下添加 Cache 装饰器的方法,在
CacheBuilder.build()
方法中进行添加:
1public class CacheBuilder {
2 //...........
3 // 创建缓存
4 public Cache build() {
5 // 设置缓存的实现类
6 setDefaultImplementations();
7 Cache cache = newBaseCacheInstance(implementation, id);
8 setCacheProperties(cache);
9 // 添加装饰器类
10 if (PerpetualCache.class.equals(cache.getClass())) {
11 for (Class? extends Cache decorator : decorators) {
12 cache = newCacheDecoratorInstance(decorator, cache);
13 setCacheProperties(cache);
14 }
15 // 为 Cache 添加装饰器
16 cache = setStandardDecorators(cache);
17 } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
18 cache = new LoggingCache(cache);
19 }
20 return cache;
21 }
22 // 设置 Cache 的默认实现类为 PerpetualCache
23 private void setDefaultImplementations() {
24 if (implementation == null) {
25 implementation = PerpetualCache.class;
26 if (decorators.isEmpty()) {
27 decorators.add(LruCache.class);
28 }
29 }
30 }
31 // 添加装饰器
32 private Cache setStandardDecorators(Cache cache) {
33 try {
34 // 添加 ScheduledCache 装饰器
35 if (clearInterval != null) {
36 cache = new ScheduledCache(cache);
37 ((ScheduledCache) cache).setClearInterval(clearInterval);
38 }
39 // 添加SerializedCache装饰器
40 if (readWrite) {
41 cache = new SerializedCache(cache);
42 }
43 // 添加 LoggingCache 装饰器
44 cache = new LoggingCache(cache);
45 // 添加 SynchronizedCache 装饰器,保证线程安全
46 cache = new SynchronizedCache(cache);
47 if (blocking) {
48 // 添加 BlockingCache 装饰器
49 cache = new BlockingCache(cache);
50 }
51 return cache;
52 }
53}
还有其他的装饰器,这里就不一一列出来了。
到这里 Mybatis 的缓存系统模块就分析完毕了。
原文始发于微信公众号(Java技术大杂烩):