redis缓存注解
Spring Boot缓存注解@Cacheable、@CacheEvict、@CachePut使用
@Cacheable(查询)
@CachePut(新增修改)
@CacheEvict(清空缓存)
一.概念
Spring Boot缓存注解@Cacheable、@CacheEvict、@CachePut使用
1、@Cacheable
可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。
@Cacheable可以指定三个属性,value、key和condition。
参数 | 解释 | example |
---|---|---|
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 | 例如: @Cacheable(value=”mycache”) @Cacheable(value={”cache1”,”cache2”} |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 | @Cacheable(value=”testcache”,key=”#userName”) |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 | @Cacheable(value=”testcache”,condition=”#userName.length()>2”) |
除了上述使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。通过该root对象我们可以获取到以下信息。
属性名称 | 描述 | 示例 |
---|---|---|
methodName | 当前方法名 | #root.methodName |
method | 当前方法 | #root.method.name |
target | 当前被调用的对象 | #root.target |
targetClass | 当前被调用的对象的class | #root.targetClass |
args | 当前方法参数组成的数组 | #root.args[0] |
caches | 当前被调用的方法使用的Cache | #root.caches[0].name |
当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。
2、 @CachePut
@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。
3、@CacheEvict
@CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性类似。
allEntries是boolean类型,表示是否需要清除缓存中的所有元素。默认为false。
beforeInvocation属性:清除操作默认是在对应方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。使用beforeInvocation可以改变触发清除操作的时间,当我们指定该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素。
/**
* 清除缓存
* allEntries = true:是否清除所有的缓存,默认值false
* beforeInvocation:是否在方法执行之前清除缓存,默认在方法执行完成后清除缓存
* @param id
*/
@CacheEvict(value = "user",key = "#id",allEntries = true,beforeInvocation = true)
@RequestMapping("deleteCache")
public void deleteCache(Integer id){
System.out.println("-------deleteCache--------");
}
二.缓存编程
1 配置文件application.yml
spring:
redis:
host: 116.62.68.112
password: 123456
port: 6379
database: 1
####swagger相关配置
swagger:
base-package: com.chenyun
title: redis-demo
description: redis
version: 1.1
terms-of-service-url: www.bajin.com
contact:
name: 80后互联网架构师-金霖海
email: 2559226091@qq.com
2.在启动类上开启SpringBoot对缓存的支持
@EnableCaching
3.在需要缓存的类或者方法上加缓存注解 @Cacheable
方法的返回值会被以缓存的形式存储到redis中
相关代码
User类
package com.chenyun.pojo;
import java.io.Serializable;
public class User implements Serializable {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User() {
}
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
CacheController
package com.chenyun.web;
import com.chenyun.pojo.User;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class CacheController {
@Cacheable(value = "getUser")
@PostMapping("getUser")
public List<User> getUser(User user){
System.out.println("--------------getUser");
User user1 = new User(1, "赵日天");
User user2 = new User(2, "李杀神");
User user3 = new User(3, "刘斩仙");
ArrayList<User> arrayList = new ArrayList<>();
arrayList.add(user1);
arrayList.add(user2);
arrayList.add(user3);
return arrayList;
}
@Cacheable(value = "getUserById",key = "#user.id")
@PostMapping("getUserById")
public List<User> getUserById(@RequestBody User user){
System.out.println("--------------getUserById");
User user1 = new User(1, "赵日天");
User user2 = new User(2, "李杀神");
User user3 = new User(3, "刘斩仙");
ArrayList<User> arrayList = new ArrayList<>();
arrayList.add(user1);
arrayList.add(user2);
arrayList.add(user3);
return arrayList;
}
/**
* 根据主键查询用户信息
*/
@Cacheable(value = "user",key = "#id")
@PostMapping("getUserById/{id}")
public User getUserById(@PathVariable("id") Integer id){
System.out.println("------------getUserById");
User user1 = new User(1, "赵日天");
User user2 = new User(2, "李杀神");
User user3 = new User(3, "刘斩仙");
if(id == 1){
return user1;
}else if(id == 2){
return user2;
}else if(id == 3) {
return user3;
}
return null;
}
/**
* 取多个参数作为组合键
* @param idOne
* @param idTwo
* @return
*/
@Cacheable(value = "user",key = "#idOne+#idTwo")
@PostMapping("getUserById2/{id1}/{id2}")
public List<User> getUserById2(@PathVariable("id1") String idOne, @PathVariable("id2") String idTwo){
System.out.println("--------------getUserById:"+idOne+"-"+idTwo);
User user1 = new User(1, "赵日天");
User user2 = new User(2, "李杀神");
User user3 = new User(3, "刘斩仙");
ArrayList<User> arrayList = new ArrayList<>();
arrayList.add(user1);
arrayList.add(user2);
arrayList.add(user3);
return arrayList;
}
/**
* 取多个参数作为组合键
* @param idOne
* @param idTwo
* @return
*/
@Cacheable(value = "user",key = "{#idOne,#idTwo}",condition="#idOne=='2'")
@PostMapping("getUserById3/{id1}/{id2}")
public List<User> getUserById3(@PathVariable("id1") Integer idOne,@PathVariable("id2") Integer idTwo){
System.out.println("--------------getUserById:"+idOne+"-"+idTwo);
User user1 = new User(1, "赵日天");
User user2 = new User(2, "李杀神");
User user3 = new User(3, "刘斩仙");
ArrayList<User> arrayList = new ArrayList<>();
arrayList.add(user1);
arrayList.add(user2);
arrayList.add(user3);
return arrayList;
}
/**
* 使用root作为组合键
* @param idOne
* @param idTwo
* @return
*/
@Cacheable(value = "user_root",key = "#root.method")
@PostMapping("getUserByIdRoot/{id1}/{id2}")
public List<User> getUserByIdRoot(@PathVariable("id1") String idOne,@PathVariable("id2") String idTwo){
System.out.println("--------------getUserById:"+idOne+"-"+idTwo);
User user1 = new User(1, "赵日天");
User user2 = new User(2, "李杀神");
User user3 = new User(3, "刘斩仙");
ArrayList<User> arrayList = new ArrayList<>();
arrayList.add(user1);
arrayList.add(user2);
arrayList.add(user3);
return arrayList;
}
//新增-修改-------------------------------------------------------------------
@CachePut(value = "user",key = "#user.id")
@PostMapping("updateUser")
public User updateUser(@RequestBody User user){
System.out.println("-----------updateUser");
return user;
}
@CachePut(value = "user",key = "#user.id")
@PostMapping("add")
public User add(User user){
return user;
}
@CacheEvict(value = "user",key = "#id")
@PostMapping("deleteCache/{id}")
public void deleteCache(@PathVariable("id") Integer id){
}
}
三 设置缓存有效期
1 配置文件
package com.chenyun.web;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.text.DecimalFormat;
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;
@Configuration
public class CacheConfig {
@Autowired
private ApplicationContext applicationContext;
@Autowired
private RedisConnectionFactory factory;
@Bean
@Primary
RedisCacheManager redisCacheManager() {
final RedisCacheWriter writer = RedisCacheWriter.nonLockingRedisCacheWriter(factory);
final GenericJackson2JsonRedisSerializer jackson = new GenericJackson2JsonRedisSerializer();
final RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair
.fromSerializer(jackson);
final RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(pair).entryTtl(Duration.ofSeconds(30));
final Map<String, RedisCacheConfiguration> conf = getConf();
System.out.println(conf);
return new RedisCacheManager(writer, config, conf);
}
@Bean
public ConcurrentMapCacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
return cacheManager;
}
private Map<String, RedisCacheConfiguration> getConf() {
final Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(Component.class);
final List<CacheMap> collect = beansWithAnnotation.entrySet().stream().flatMap(entry -> {
try {
final Object value = entry.getValue();
// 获得原本的class名字,spring代理的都是后面有$$直接截取即可
String className = "";
if (value.getClass().getName().contains("$")){
className = value.getClass().getName().
substring(0, value.getClass().getName().indexOf("$"));
}else{
className = value.getClass().getName();
}
// 获得原始的字节码文件,如果被spring 代理之后,方法上会获取不到注解信息
final Method[] methods = Class.forName(className)
.getDeclaredMethods();
return Arrays.stream(methods).flatMap(method -> {
final Cacheable annotation = method.getAnnotation(Cacheable.class);
if (annotation == null) {
return null;
}
return Arrays.stream(annotation.cacheNames()).map(data -> {
if (data.contains("#")) {
// 包含自定义日期
final String[] split = data.split("#");
if (split.length != 2) {
return null;
}
final CacheMap cacheMap = new CacheMap();
cacheMap.setName(data);
final String s = split[1];
final int time = Integer.parseInt(s.substring(0, s.length() - 1));
if (s.endsWith("s")) {
cacheMap.setTtl(Duration.ofSeconds(time));
} else if (s.endsWith("m")) {
cacheMap.setTtl(Duration.ofMinutes(time));
} else if (s.endsWith("d")) {
cacheMap.setTtl(Duration.ofDays(time));
}
return cacheMap;
}
return null;
}).filter(Objects::nonNull);
});
} catch (Exception e) {
System.out.println("异常");
return null;
}
}).collect(Collectors.toList());
return collect.stream().collect(Collectors.toMap(CacheMap::getName, p -> {
final GenericJackson2JsonRedisSerializer jackson = new GenericJackson2JsonRedisSerializer();
final RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair
.fromSerializer(jackson);
return RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(pair).entryTtl(p.getTtl());
}, (key1, key2) -> key2));
}
class CacheMap {
private String name;
private Duration ttl;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Duration getTtl() {
return ttl;
}
public void setTtl(Duration ttl) {
this.ttl = ttl;
}
}
}
2 Controller
package com.chenyun.web;
import com.chenyun.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
public class TimeCacheController{
@Autowired
private StringRedisTemplate redisTemplate;
@PostMapping("getUserByIdTime")
public User getUserByIdTime(){
System.out.println("------------getUserById");
User user1 = new User(1, "赵日天");
//k v t u
redisTemplate.opsForValue().set("user::1",user1.toString(),1, TimeUnit.MINUTES);
return user1;
}
@Cacheable(value = "user#30s",key = "#id")
@PostMapping("getUserByIdTime/{id}")
public User getUserByIdTime(@PathVariable("id") Integer id){
System.out.println("------------getUserById");
User user1 = new User(1, "赵日天");
User user2 = new User(2, "李杀神");
User user3 = new User(3, "刘斩仙");
if(id == 1){
return user1;
}else if(id == 2){
return user2;
}else if(id == 3) {
return user3;
}
return null;
}
}