戴老师的博客 - 缓存
type: channel
link: https://www.tushu.info/tag/%E7%BC%93%E5%AD%98/
items: 注释驱动的 Spring cache 缓存介绍
items: MyBatis 缓存机制深度解剖

注释驱动的 Spring cache 缓存介绍
type: item
date: 2026-07-07T08:28:41+00:00
description: 概述Spring 3.1 引入了基于注释（Annotation）的缓存（Cache）技术。它本质上不是一个具体的缓存实现方案（例如 EHCache 或者 OSCache），而是一个对缓存使用的抽象。通过在既有代码中添加少量 Spring 定义的注释，即可达到缓存方法返回对象的效果。Spring 的缓存技术具备相当的灵活性：不仅能够使用 SpEL（Spring Expression Language）来定义缓存的 key 和各种 condition，还提供开箱即用的缓存临时存储方案，同时也支持和主流的专业缓存（例如 EHCache）集成。其特点总结如下：低侵入性：通过少量的配置 Annotation 注释即可使得既有代码支持缓存。开箱即用（Out-Of-The-Box）：不用安装和部署额外第三方组件即可使用缓存。支持 SpEL：能使用对象的任何属性或者方法来定义缓存的 key 和 condition。支持 AspectJ：并通过其实现任何方法的缓存支持。高扩展性：支持自定义 key 和自定义缓存管理者，具有相当的灵活性和扩展性。本文将针对上述特点对 Spring Cache 进行详细介绍。主要通过一个简单的例子和原理介绍展开，随后展示一个比较实际的缓存例子，最后介绍 Spring Cache 的使用限制和注意事项。原来我们是怎么做的这里先展示一个完全自定义的缓存实现，即不用任何第三方组件来实现某种对象的内存缓存。场景：对一个账号查询方法做缓存，以账号名称为 key，账号对象为 value。当以相同的账号名称查询账号的时候，直接从缓存中返回结果，否则更新缓存。账号查询服务还支持 reload 缓存（即清空缓存）。首先定义一个实体类：账号类，具备基本的 id 和 name 属性，且具备 getter 和 setter 方法。清单 1. Account.javapackage cacheOfAnno; public class Account { private int id; private String name; public Account(String name.html) { this.name = name; } public int getId() { return id; } public void setId(int id.html) { this.id = id; } public String getName() { return name; } public void setName(String name.html) { this.name = name; } }然后定义一个缓存管理器，这个管理器负责实现缓存逻辑，支持对象的增加、修改和删除，支持值对象的泛型。清单 2. MyCacheManager.javapackage oldcache; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class MyCacheManager<T> { private Map<String,T> cache = new ConcurrentHashMap<String,T>(); public T getValue(Object key.html) { return cache.get(key.html); } public void addOrUpdateCache(String key,T value.html) { cache.put(key, value.html); } public void evictCache(String key.html) {// 根据 key 来删除缓存中的一条记录 if(cache.containsKey(key.html)) { cache.remove(key.html); } } public void evictCache() {// 清空缓存中的所有记录 cache.clear(); } }现在我们有了实体类和一个缓存管理器，还需要一个提供账号查询的服务类。此服务类使用缓存管理器来支持账号查询缓存。清单 3. MyAccountService.javapackage oldcache; import cacheOfAnno.Account; public class MyAccountService { private MyCacheManager<Account> cacheManager; public MyAccountService() { cacheManager = new MyCacheManager<Account>();// 构造一个缓存管理器 } …

MyBatis 缓存机制深度解剖
type: item
date: 2026-07-07T08:26:50+00:00
description: 编者注：本文为历史博文归档；涉及 JDK、框架与工具链版本请以当前官方文档为准。引用外链图片可能失效，阅读时请注意时效性。缓存概述正如大多数持久层框架一样，MyBatis 同样提供了一级缓存和二级缓存的支持：一级缓存：基于 PerpetualCache 的 HashMap 本地缓存。其存储作用域为 Session，当 Session flush 或 close 之后，该 Session 中的所有 Cache 将被清空。二级缓存：与一级缓存机制相同，默认也是采用 PerpetualCache（HashMap 存储）。不同在于其存储作用域为 Mapper (Namespace.html)，并且可自定义存储源，如 Ehcache、Hazelcast 等。缓存更新机制：当某一个作用域（一级缓存 Session / 二级缓存 Namespaces）进行了 C/U/D 操作后，默认该作用域下所有 select 中的缓存将被 clear。设计模式：MyBatis 的缓存采用了 Delegate 机制及装饰器模式设计。当 put、get、remove 时，其中会经过多层 Delegate Cache 处理。其 Cache 类别主要有：BaseCache（基础缓存）：为缓存数据最终存储的处理类，默认为 PerpetualCache，基于 Map 存储；可自定义存储处理，如基于 EhCache、Memcached 等。EvictionCache（排除算法缓存）：当缓存数量达到一定大小后，将通过算法对缓存数据进行清除。默认采用 LRU 算法 (LruCache.html)，提供有 FIFO 算法 (FifoCache.html) 等。DecoratorCache（装饰器缓存）：缓存 put/get 处理前后的装饰器。例如使用 LoggingCache 输出缓存命中日志信息、使用 SerializedCache 对 Cache 的数据 put 或 get 进行序列化及反序列化处理、当设置 flushInterval (默认 1 小时.html) 后，则使用 ScheduledCache 对缓存数据进行定时刷新等。Key 生成规则：一般缓存框架的数据结构基本上都是 Key-Value 方式存储，MyBatis 对于其 Key 的生成采取规则为：[hashcode : checksum : mappedStatementId : offset : limit : executeSql : queryParams]。并发同步：对于并发 Read/Write 时缓存数据的同步问题，MyBatis 默认基于 JDK concurrent 中的 ReadWriteLock，使用 ReentrantReadWriteLock 的实现，从而通过 Lock 机制防止在并发 Write Cache 过程中出现线程安全问题。源码剖解接下来将结合 MyBatis 序列图进行源码分析。在分析其 Cache 前，先看看其整个处理过程。执行过程通常情况下，我们需要在 Service 层调用 Mapper Interface 中的方法实现对数据库的操作，上述根据产品 ID 获取 Product 对象。当调用 ProductMapper 中的方法时，其实这里所调用的是 MapperProxy 中的方法，并且 MapperProxy 已经将所有的方法拦截。其具体原理及分析，参考 MyBatis+Spring 基于接口编程的原理分析，其 invoke 方法代码为：// 当调用 Mapper 所有的方法时，将都交由 Proxy 中的 invoke 处理 public Object invoke(Object proxy, Method method, Object[] args.html) throws Throwable { try { if (!OBJECT_METHODS.contains(method.getName(.html))) { final Class declaringInterface = findDeclaringInterface(proxy, method.html); // 最终交由 MapperMethod 类处理数据库操作，初始化 MapperMethod 对象 final MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, sqlSession.html); // 执行 mapper method，返回执行结果 final Object result = mapperMethod.execute(args.html); .... return result; }…