SpringBoot与缓存
几个重要的概念 & 缓存注解
|
Cache |
缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、ConcurrentMapCache等 |
|
CacheManager |
缓存管理器,管理各种缓存(Cache)组件 |
|
@Cacheable |
主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 |
|
@CacheEvict |
清空缓存 |
|
@CachePut |
保证方法调用,又能将结果缓存,可以用于刷新缓存 |
|
@EnableCaching |
开启基于注解的缓存 |
|
keyGenerator |
缓存数据时key生成策略 |
|
serialize |
缓存数据时value序列化的策略 |
使用缓存
首先引入cache组件

@Cacheable
将方法的运行结果进行缓存,以后要相同的数据,直接从缓存中获取,不用调用方法
- CacheManager管理多个Cache组件的,每一个缓存组件有自己唯一的名字
- 几个属性:
- cacheNames/value:指定缓存组件的名字 cacheNames = {"emp"}
- key:缓存数据使用的key,默认使用的是方法的参数、还可以使用SpEL来编写
- keyGenerator:key的生成器,生成缓存数据的key,keyGenerator跟key只能二选一
- cacheManager:指定缓存管理器,或者cacheResolver获取解析器
- condition:指定符合条件的情况下才缓存。例:condition="#id>0",id大于0才缓存
- unless:否定缓存,当unless指定的条件为true,方法的返回值就不会被缓存,可以用#result获取方法的返回值。例: unless = "#result == null"
- sync: 是否使用异步模式
key的取值
|
名字 |
描述 |
示例 |
|
methodName |
当前被调用的方法名 |
#root.methodName |
|
method |
当前被调用的方法 |
#root.method.name |
|
target |
当前被调用的目标对象 |
#root.target |
|
targetClass |
当前被调用的目标对象类 |
#root.targetClass |
|
args |
当前被调用的方法的参数列表 |
#root.args[0] |
|
caches |
当前方法调用使用的缓存列表(如@Cacheable(value={“cache1”, “cache2”})),则有两个cache |
#root.caches[0].name |
|
argument name |
方法参数的名字,可以直接 #参数名,也可以使用 #p0或#a0 的形式,0代表参数的索引 |
#iban、#a0, #p0 |
|
result |
方法执行后的返回值(仅当前方法执行之后的判断有效,如’unless’, ‘cache put’的表达式 ‘cache evict’的表达式beforeInvocation=false) |
#result |
例:
①、首先在主程序类上标注上@EnableCaching注解
@EnableCaching
@SpringBootApplication
public class SpringBootCacheApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootCacheApplication.class, args);
}
}
②、在要缓存的方法上标注即可
@Cacheable(cacheNames = {"emp"})
public Employee getEmp(Integer id){
System.out.println("查询" + id + "员工号");
Employee emp = employeeMapper.getEmployeeById(id);
return emp;
}
@CachePut
这个注解用于更新缓存
示例:
/**
* @CachePut: 既调用方法,又更新缓存
* 主要用于修改了数据库的某个数据,同时更新缓存
* 运行时机:先调用目标方法,再将目标方法的结果缓存起来
* 与@Cacheable不同,@Cacheable是先去缓存里面查看是否有值,再执行方法,因而这里在获取id的时候@CachePut可以通过#result获取而@Cacleable不行
* 注意:这里的key一定要填要更新的缓存的key,不填就会默认使用第一个参数作为key,很可能就达不到更新缓存的目的而是新创建了一个缓存
*/
@CachePut(value = "emp", key = "#result.id")
public Employee updateEmp(Employee employee){
System.out.println("更新cache");
employeeMapper.updateEmp(employee);
return employee;
}
@CacheEvict
该注解用于删除一个缓存
示例:
/**
* allEntries:删除所有的缓存 allEntries = true
* beforeInvocation:缓存的清除是否在方法之前执行,默认是在方法之后执行
* 其他参数与@Cacheable大致一样
* @param id
*/
@CacheEvict(value = "emp", key = "#id", allEntries = true)
public void deleteEmp(Integer id){
System.out.println("删除" + id);
}
@Caching
该注解用于存放多个缓存注解,有时候对于一个方法,想放置多个缓存,既想缓存又想更新时使用
示例:
/**
* 可以放多个注解
* @param lastName
* @return
*/
@Caching(
cacheable = {
@Cacheable(value = "emp", key = "#lastName")
},
put = {
@CachePut(value = "emp", key = "#result.id"),
@CachePut(value = "emp", key = "#result.email")
}
)
public Employee getEmpByLastName(String lastName){
System.out.println("查询");
return employeeMapper.getEmpByLastName(lastName);
}
@CacheConfig
有的时候觉得每次在使用缓存注解的时候都要指定缓存的名字,或者指定缓存的cacheManager之类的,觉得很麻烦。那么就可以在类上使用@CacheConfig统一的配置缓存的名字
@CacheConfig(value = "emp")
@Service
public class EmployeeService {
自定义key的生成策略
对于key,我们可以让它自动生成,生成的策略可以有我们自己制定,之需要在配置类中将我们的定制规则加入到容器中即可
@Bean
public KeyGenerator keyGenerator(){
return new KeyGenerator(){
@Override
public Object generate(Object o, Method method, Object... objects) {
return method.getName() + "[" + Arrays.asList(objects) + "]";
}
};
}
使用Redis做为缓存
SpringBoot默认使用的是ConcurrentMapCacheManager==ConcurrentMapCache,将数据保存在ConcurrentMap<String, Cache> cacheMap里面
当然我们还可以使用其他的中间件作为缓存
开发中使用的缓存中间件:redis,memcached,ehcache
引入redis的starter
<!-- 引入redis的starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置redis
来到配置文件application.yaml配置redis的主机地址
spring:
redis:
host: 172.17.119.176
使用redis
springboot的starter提供了两个Template操作redis
StringRedisTemplate跟RedisTemplate,其中StringRedisTemplate的key跟value都是字符串。redisTemplate的key跟value都是Object
/**
* String(字符串)、List(列表)、Set(集合)、Hash(散列)、ZSet(有序集合)、HyperLogLog
* stringRedisTemplate.opsForValue() 操作String(字符串)
* stringRedisTemplate.opsForList() 操作List(列表)
* stringRedisTemplate.opsForSet() 操作Set(集合)
* stringRedisTemplate.opsForHash() 操作Hash(散列)
* stringRedisTemplate.opsForZSet() 操作ZSet(有序集合)
* stringRedisTemplate.opsForHyperLogLog() 操作HyperLogLog
*/
@Test
public void test01(){
stringRedisTemplate.opsForValue().append("redis", "hello");
stringRedisTemplate.opsForList();
stringRedisTemplate.opsForSet();
stringRedisTemplate.opsForHash();
stringRedisTemplate.opsForZSet();
stringRedisTemplate.opsForHyperLogLog();
}
储存对象
有的时候需要将对象存进redis(例如一个JavaBean对象),但是如果对象不是可Serializable的,因此需要让JavaBean对象实现Serializable接口
public class Employee implements Serializable {
序列化机制
如果只是让JavaBean实现Serializable接口也是可以存储的,但是并不好看,那么能不能将JavaBean弄成Json的样式放进redis呢。直接的方式就是自己转换,但是未免有点麻烦,那就只能修改RedisTemplate的序列化机制了,在配置类中配置上序列化的方法即可
@Bean
public RedisTemplate<Object, Employee> empredisTemplate(
RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Employee> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer<Employee> ser = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
redisTemplate.setDefaultSerializer(ser);
return redisTemplate;
}
自定义CacheManager
对于自定义CacheManager,SpringBoot 1.x跟SpringBoot 2.x有所不同
SpringBoot 1.x
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
//默认超时时间,单位秒
redisCacheManager.setDefaultExpiration(60);
//缓存超时时间Map,key为cacheName,value为超时,单位是秒
Map<String, Long> expiresMap = new HashMap<>();
//缓存用户信息的cacheName和超时时间
expiresMap.put("user", 1800L);
//缓存产品信息的cacheName和超时时间
expiresMap.put("product", 600L);
redisCacheManager.setExpires(expiresMap);
return redisCacheManager;
}
SpringBoot 2.x
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1)); // 设置缓存有效期一小时
return RedisCacheManager
.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
.cacheDefaults(redisCacheConfiguration).build();
}
编码的方式使用缓存
前面都是用的注解的方法将注解标注在方法上去缓存方法的结果,但是如果在方法的执行中有一个数据我们想缓存那应该怎么办呢?既然前面提到过CacheManager是管理Cache的,那我们就可以直接使用CacheManager去缓存了
@Autowired
RedisCacheManager redisCacheManager;
public void test(){
Cache emp = redisCacheManager.getCache("emp");
emp.put("111", "222");
}

浙公网安备 33010602011771号