1、JSR-107规范
1.1 JSP-107是什么?
要回答这个问题,首先要知道JSR是什么,JSR是Java Specification Requests 的缩写 ,Java规范请求,故名思议就是Java规范,大家一同遵守这个规范的话,会让大家‘沟通’起来更加轻松。规范是很重要的 ,举个例子大家都知道红灯停,路灯行吧,如果每个城市的信号灯代表不一样,那就麻烦了,B城市红灯行,绿灯停,C城市甚至出现紫灯行,闪灯行,想想都知道,如果我们保证不出问题,必须知道每个城市的信号等代表的意义。我们一直使用的JDBC就一个访问数据库的一个规范的例子。 而 JSR-107呢就是关于如何使用缓存的规范。
1.2 JSR-107核心API
Java Caching定义了5个核心接口,分别是CachingProvider
,CacheManager
,Cache
,Entry
和Expiry
。
-
CachingProvider 用于定义创建、配置、获取、管理和控制Cachemanager。
-
CacheManager 用于定义了建立,配置,得到,管理和控制有着唯一名字的Cache ,一个CacheManager被包含在单一的CachingProvider。
-
Cache Cache是一个Map类型的数据结构,用来存储基于键的数据,很多方面都像java.util.Map数据类型。一个Cache 存在在单一的CacheManager。
-
Entry Entry是一个存在于Cache的key-value键值对
-
Expiry 每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy来设置。
这些接口之间的关系可以用下图表示:

2、Spring缓存抽象
Spring 3.1从开始定义了org.springframework.cache.Cache
和org.springframework.cache.CacheManager
接口来统一不同的缓存技术,并使用JCache(JSR-107)注解简化我们的开发。
- Cache接口为缓存的组件规范定义,包含缓存的各种操作集合
- Cache接口下Spring提供了各种xxxCache的实现,如RedisCache、EncheCache、ConcurrentMapCache......
- 每次调用需要缓存功能的方法是,Spring会检查指定的参数的是定目标方法时候已经被调用过,如果有就直接从缓存中获取方法调用后对结果,如果没有就调用方法去数据库查询并缓存结果后返回给用户,下次回直接从缓存中获取。
重要的缓存注解
注解 | 功能 |
---|---|
Cache | 缓存接口,定义缓存操作。 |
CacheManager | 缓存管理器。管理和中缓存组件 |
@Cacheable | 主要用于方法,能够根据方法的请求参数对其进行缓存 |
@CachePut | 方法被调用,并且在调用后结果被缓存,主要用于更新操作 |
@CacheEvict | 清除缓存 |
@Caching | 配置复杂对缓存策略 |
@CacheConfig | 同一配置本类的缓存注解额属性 |
@EnableCaching | 开启基于注解的缓存 |
serialize | 缓存数据的value序列haul策略 |
@Cacheable/@CachePut/@CacheEvict 主要的参数
参数 | 解释 |
---|---|
value/cacheNames | 缓存的名字。必须指定至少一个,可以配置多个 例如: @Cacheable(value={"cache1","cache2"}) |
key | 缓存的key。可以为空,如果指定要使用SpEL。默认将方法的所有参数组合起来作为key。 例如: @Cacheable(value="cache1",key="#id") |
keyGenerator | 定义自动生成主键的策略,使用的时候key和keyGenerator二选一 |
condition | 作缓存的条件。可以为空,使用SpEL表达式指定,返回true表示作缓存,否者不缓存。 例如: @Cacheable(vlaue="cache",condition="#id>0") |
unless | 也是作缓存的条件。当条件为true时,就不缓存(和condition的效果是反的)。 例如:@Cacheable(value="cache",unless="#id<0") |
sync (@Cacheable) | 是否使用异步支持,这是Spring 4.3以后才支持的,默认值false,不开启异步模式 例如: @Cacheable(value="cache",sync=true) //开启异步模式 |
allEntries (@CacheEvict) | 是否清空所有缓存内容。默认为false,如果指定为true,则方法调用后将立即清空所有缓存。 |
beforeInvocation (@CacheEvict) | 是否在方法执行前清空缓存。默认为false,如果指定为true,则方法还没有执行的时候就清空缓存。默认情况下如果方法抛出异常,就没有办法清空缓存了。 |
SpEL上下文数据,Spring 提供了一些供我们使用的SpEL表达式,
名称 | 位置 | 描述 | 用法示例 |
---|---|---|---|
methodName(方法名) | root对象 | 当前被调用的方法名 | #root.methodname |
method(方法) | root对象 | 当前被调用的方法 | #root.method.name |
target(当前对象) | root对象 | 当前被调用的目标对象实例 | #root.target |
targetClass(目标类) | root对象 | 当前被调用的目标对象的类 | #root.targetClass |
args(参数列表) | root对象 | 当前被调用的方法的参数列表 | #root.args[0] |
caches(缓存列表) | root对象 | 当前方法调用使用的缓存列表 | #root.caches[0].name |
Argument Name(参数名) | 执行上下文 | 当前被调用的方法的参数,如findArtisan(Artisan artisan),可以通过#artsian.id获得参数 | #artsian.id |
result(方法返回值) | 执行上下文 | 方法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false) | #result |
3、 Spring缓存的配置和使用
3.1 @Cacheable
@Cacheable注解会先查询是否已经有缓存,有会使用缓存,没有则会执行方法并缓存。
@Cacheable(value="user",key = "#root.methodName+'('+#id+')'")
@Override
public User get(Integer id) {
return userMapper.selectUserById(id);
}
在CacheAspectSupport这个类的findCachedItem上打上断点,观察发下我们配置的key是有效:

3.2 自定义主键生成策略
package com.xust.iot.learningspirngbootcache01.config;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
@Configuration
public class CacheConfig {
@Bean("userKey") //bean的名字就是主键生成策略的名字
public KeyGenerator keyGenerator(){
//主键策略:方法的hashcode+@+[参数]
return (target, method, params) -> method.getName().hashCode()+"@"+ Arrays.toString(params);
}
}
在方法中可以这么使用:
@Cacheable(value="user",keyGenerator = "userKey")
@Override
public User get(Integer id) {
return userMapper.selectUserById(id);
}
同样,通过打断点发现我们配置的主键生成也起作用了:

其他参数的用法可以参考下面的程序:
/**
*自定义缓存策略,并且只有当返回值不是null的时候才缓存
*/
@Cacheable(value="user",keyGenerator = "userKey",condition = "#result!=null")
@Override
public User get(Integer id) {
return userMapper.selectUserById(id);
}
/**
*自定义缓存策略,并且当返回值是null的时候不缓存,也就相当于上面的写法,只不过unless是判断为false的时候才做缓存
*/
@Cacheable(value="user",keyGenerator = "userKey",unless = "#result==null")
@Override
public User get(Integer id) {
return userMapper.selectUserById(id);
}
/**
*自定义缓存策略,并且开启异步缓存
*/
@Cacheable(value="user",keyGenerator = "userKey",sync = true)
@Override
public User get(Integer id) {
return userMapper.selectUserById(id);
}
3.3 @CachePut
@CachePut注解的作用主要用于对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用 。简单来说就是用户更新缓存数据。但需要注意的是该注解的value 和 key 必须与要更新的缓存相同,也就是与@Cacheable 相同。
//方法本调用后,总是会执行方法,返回后如果条件瞒住就来缓存
@CachePut(value = "user",keyGenerator = "userKey",condition = "#result==true")
@Override
public boolean update(Integer id, User user) {
return userMapper.updateUserById(id,user);
}
@CachePut的其他参数的用法和一样@Cacheable参数的用法一样。这里不再赘述。
3.4 @CacheEvict
@CachEvict 的作用主要用于方法的配置,能够根据一定的条件对缓存进行清空 。
@CacheEvict(value = "user",beforeInvocation = false) //默认就是false,会在方法执行后清缓存
public boolean delete(Integer id) {
int i/0; //这里发生异常了,缓存无法清空
return userMapper.deleteUserById(id);
}
@CacheEvict(value = "user",beforeInvocation = true) //在方法执行前清除缓存
@Override
public boolean delete(Integer id) {
int i/0; //即使这里会发生异常,还是会清空缓存,因为清除缓存是在方法执行前执行的
return userMapper.deleteUserById(id);
}
@CacheEvict(value = "user",allEntries = true) //方法调用后清空所有缓存
@Override
public boolean delete(Integer id) {
return userMapper.deleteUserById(id);
}
@CacheEvict(value = "user",allEntries = false) //默认不清空所有缓存
@Override
public boolean delete(Integer id) {
return userMapper.deleteUserById(id);
}
3.5 @Caching
有时候我们可能组合多个Cache注解使用,此时就需要@Caching组合多个注解标签了。
//组合缓存策略,Caching只有下面三个属性
@Caching(cacheable = {
@Cacheable(value = "user", keyGenerator = "userKey")
},
put = {
@CachePut(value = "user", keyGenerator = "userKey")
},
evict = {
@CacheEvict(beforeInvocation = true, allEntries = true)
}
)
@Override
public boolean delete(Integer id) {
return userMapper.deleteUserById(id);
}
3.6 @CachingConfig
当我们需要缓存的地方越来越多,你可以使用@CacheConfig(cacheNames = {"cacheName"})注解来统一指定value/cacheNames的值,这时可省略value/cacheNames,如果你在你的方法依旧写上了value/cacheNames,那么依然以方法的@CacheConfig配置的值为准。使用方法如下:
@CacheConfig(cacheNames = "user") //在类名头上只用@CacheConfig来指定这个类全局的缓存的名字
@Service
public class UserServiceImpl implements IUserService<User> {
@Autowired
UserMapper userMapper;
@Cacheable(keyGenerator = "userKey", sync = true)
@Override
public User get(Integer id) {
return userMapper.selectUserById(id);
}
@CachePut(value = "user", keyGenerator = "userKey", condition = "#result==true")
@Override
public boolean update(Integer id, User user) {
return userMapper.updateUserById(id, user);
}
}
@CacheConfig可以配置单的属性如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheConfig {
String[] cacheNames() default {}; //缓存名字,它里面没有value
String keyGenerator() default ""; //主键生成策略
String cacheManager() default ""; //指定缓存管理器
String cacheResolver() default ""; //缓存处理器
}