1、添加依赖
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.4.1</version>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.13.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>2.4.1</version>
</dependency>
2、添加配置
application.yml
spring:
redis:
host: 192.168.43.129
port: 6379
cache:
type: redis
application类添加@EnableCaching开启缓存注解
@EnableCaching
@SpringBootApplication
public class ApplicationManagementApplication {
public static void main(String[] args) {
SpringApplication.run(ApplicationManagementApplication.class, args);
}
}
3、使用SpringCache
3.1、@Cacheable保存数据
如果缓存中有,则直接返回缓存数据,如果缓存中没有,执行方法体,将方法返回值存入缓存
/**
*Cacheable:保存缓存,如果缓存中有,则直接返回缓存数据,如果缓存中没有,执行方法体,
* 将方法返回值存入缓存
*testCache: 缓存的名字
*/
@Cacheable(value = "testCache")
@RequestMapping("test_cacheable")
public String testCacheable(){
return "cacheable";
}
Redis中缓存结果
- key:testCache::SimpleKey []。缓存的名字+::+自动生成的key值
- value:\xAC\xED\x00\x05t\x00\x09cacheable。默认使用JDK序列化后的数据。
-
TTL:-1。默认永不过期
Cache1.jpg
自定义缓存规则
- 指定缓存生成的key:key属性,接收一个SpEL表达式。key = "#id"使用参数id作为key
@Cacheable(value = "testCache",key = "#id")
@RequestMapping("test_cacheable")
public String testCacheable(Integer id){
System.out.println("进入方法体");
return "cacheable"+id;
}
- 指定缓存过期时间:application.yml中指定spring.cache.redis.time-to-live,以ms为单位
spring:
redis:
host: 192.168.43.129
port: 6379
cache:
type: redis
redis:
time-to-live: 3600000
访问http://localhost:8080/test_cacheable?id=1,redis中缓存结果:key为缓存名字+::+参数id的值,并且TTL过期时间为1个小时。

- 将数据保存为Json格式:
为了效果更加明显,让接口返回一个对象,在controller中新建内部类user
@Cacheable(value = "testCache",key = "#id")
@RequestMapping("test_cacheable")
public User testCacheable(Integer id){
System.out.println("进入方法体");
User user = new User();
user.setName("Jessie");
user.setAge(18);
return user;
}
@Data
class User{
String name;
int age;
}
新建缓存配置类MyCacheConfig,配置类会让之前yml文件中的配置失效,因此需要把之前配置文件中TTL的配置,在配置类中重新配置一遍。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
/**
* @author zbw
*/
@Configuration
public class MyCacheConfig {
@Bean
public RedisCacheConfiguration redisCacheConfiguration(){
RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();
//默认开启缓存空值,可以防止缓存穿透
//序列化key的方式
defaultCacheConfig = defaultCacheConfig
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
//序列化value的方式
defaultCacheConfig = defaultCacheConfig
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
//每次设置配置值,都会重置其他配置为默认值,因此需要重新指定TTL
defaultCacheConfig = defaultCacheConfig.entryTtl(Duration.ofHours(1));
return defaultCacheConfig;
}
}
访问:http://localhost:8080/test_cacheable?id=1,Redis中缓存结果:

3.2、@CacheEvict清除缓存
CacheEvict失效模式:执行完方法体之后,将缓存删除,等待下一次执行@Cacheable标注的方法时,再将缓存存入Redis中。
@CacheEvict(value = "testCache",key = "#id")
@RequestMapping("update_cacheable")
public String updateCacheable(Integer id){
return "Hello CacheEvict";
}
3.3、@CachePut修改缓存
CachePut双写模式:执行完方法体之后,修改缓存中的数据为方法的返回值。
@CachePut(value = "testCache",key = "#id")
@RequestMapping("put_cacheable")
public User putCacheable(Integer id){
System.out.println("进入方法体");
User user = new User();
user.setName("Jessie");
user.setAge(20);
return user;
}
3.4、@Caching整合多个缓存操作
如果在一个方法里面,需要清除多个缓存,可以使用@Caching,将多条@CacheEvict语句整合在一起。
@Caching(evict = {
@CacheEvict(value = "testCache",key = "'1'"),
@CacheEvict(value = "testCache",key = "'2'")
})
@RequestMapping("caching")
public String caching(){
return "Hello Caching";
}
4、使用SpringCache注意事项
从以下几点分析:
- 缓存穿透,可以通过缓存空值(cache-null-value=true)解决,默认也是缓存空值的。
- 缓存击穿,大量并发查询一条正好过期的数据时,默认是没有加锁,直接访问数据库的,因此无法解决击穿问题。可以通过添加sync=true,使得读取操作加锁执行。
@Cacheable(value = "testCache",key = "#id",sync = true)
@RequestMapping("test_cacheable")
public User testCacheable(Integer id){
System.out.println("进入方法体");
User user = new User();
user.setName("Jessie");
user.setAge(18);
return user;
}
- 缓存雪崩,通过给缓存设置同一过期时间来解决,并不一定都是同一时刻触发保存缓存的操作,缓存过期的时间一般也比较分散。
- 所有的写模式都没有加锁。
总结:对于读多写少、即时性、一致性要求没那么高的场景,完全可以使用SpringCache。但是对于特殊数据需要特殊处理。
网友评论