Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使用的抽象,通过在既有代码中添加少量它定义的各种 annotation,即能够达到缓存方法的返回对象的效果。
Spring 的缓存技术还具备相当的灵活性,不仅能够使用 SpEL(Spring Expression Language)来定义缓存的 key 和各种 condition,还提供开箱即用的缓存临时存储方案,也支持和主流的专业缓存例如 EHCache 集成。特点如下:
1、开箱即用,不需要自身添加过多的代码
2、注解驱动,使用方便
3、和业务代码做到了隔离,互相不污染
4、可以自定义缓存管理器、缓存,做到了第三方框架的良好融合;并且可以同时融合多个缓存框架
为了大家能更加直观的了解到之前和使用后的区别,我们对之前的代码和现在的做下对比:
之前的代码如下:
package oldcache; import cacheOfAnno.Account; public class MyAccountService { private MyCacheManager<Account> cacheManager; public MyAccountService() { cacheManager = new MyCacheManager<Account>();// 构造一个缓存管理器 } public Account getAccountByName(String acctName) { Account result = cacheManager.getValue(acctName);// 首先查询缓存 if(result!=null) { System.out.println("get from cache..."+acctName); return result;// 如果在缓存中,则直接返回缓存的结果 } result = getFromDB(acctName);// 否则到数据库中查询 if(result!=null) {// 将数据库查询的结果更新到缓存中 cacheManager.addOrUpdateCache(acctName, result); } return result; } public void reload() { cacheManager.evictCache(); } private Account getFromDB(String acctName) { System.out.println("real querying db..."+acctName); return new Account(acctName); } }
通过上面的代码,我们可以看到整个的缓存逻辑和业务逻辑混合在一起,代码互相污染。
使用Spring Cache 框架后,代码的整体风格如下:
package cacheOfAnno; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; public class AccountService { @Cacheable(value="accountCache",key = "#userName")// 使用了一个缓存名叫 accountCache public Account getAccountByName(String userName) { // 方法内部实现不考虑缓存逻辑,直接实现业务 System.out.println("real query account."+userName); return getFromDB(userName); } private Account getFromDB(String acctName) { System.out.println("real querying db..."+acctName); return new Account(acctName); } }
可以看到,代码更加干净,并且代码逻辑清楚。由于Spring Cache框架为注解驱动的,那么我讲对其注解进行一一说明
序号 | 注解名称 | 作用 |
1 | Cacheable | 将方法的执行结果放到缓存中。下次执行时,若是发现有缓存,则直接取缓存中的数据 |
2 | CachePut | 将方法的执行结果放到缓存中 |
3 | CacheEvict | 移除指定的缓存 |
4 | CacheConfig | 配置信息 |
5 | Cacheing | 可以指定各种类型的注解 |
6 | 自定义注解 |
注解介绍
1、Cacheable --- 参数介绍
- cacheNames :指定缓存名称
- key: 保存的键
- condition:条件。若是满足条件,则保存
- unless: 否决条件,方法执行完毕后验证,为false的时候,才保存
- cacheManger: 指定cache管理器
- cacheResolver: 按照指定的名称分析得到cache
- sync: 为true,表明若是多个线程调用此方法,只有一个线程可以访问,其他线程直接从缓存中获取。
- keyGenerator:键的生成器
2、CachePut
- cacheNames:缓存名称
- key:对应的键
- condition:条件。同上
- unless:同上
- keyGenerator:同上
- cacheResolver:同上
3、CacheEvict
- cacheNames:缓存名称
- key:对应的键
- condition:条件。同上
- unless:同上
- keyGenerator:同上
- cacheResolver:同上
- allEntries:是否全部情况
- beforeInvocation:是否在方法之前执行;若为false,不保证,能执行(例外:若是方法报异常)
4、CacheConfig:配置 。主要是配置在类上
- cacheNames:缓存的名称
- cacheManager:缓存管理器
- keyGenerator:键生成器
- cacheResolver:cache分析器
SPEL说明
Cache框架中的key、condition、unless中的表达式是按照SPEL的形式进行描述的,下面将对其中的参数进行一一说明:
序号 | name | location | 描述 |
示例 |
1 | methodName | root | 当前被调用的方法的名称 | #root.methodName |
2 | method | root | 当前被调用的方法 | #root.method |
3 | target | root | 当前被调用的实体 | #root.target |
4 | targetClass | root | 当前被调用的实体的类型 | #root.targetClass |
5 | args | Evaluation context | 当前被调用方法的参数 | #root.args[0] |
6 | caches | root | 当前被调用方法的缓存集合 | #root.caches[0].name |
7 | result | Evaluation context |
当前被调用方法的返回值,只能在unless 或是 方法执行完毕的逻辑中使用 |
#result |
8 | Argument name | Evaluation context | 当前方法的参数名称 |
#userName #p[0] #a[0] |
示例如下:
@Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result.hardback == null")
public Optional<Book> findBook(String name)