Spring Cache 介绍 看到Spring就知道这是Spring生态中的东西,其实缓存数据的技术并不少,Spring 官方此举是引入 Spring Cache 来帮我们管理缓存,使用注解,简化很多操作。
Spring Cache 介绍
看到Spring就知道这是Spring生态中的东西,其实缓存数据的技术并不少,Spring 官方此举是引入 Spring Cache 来帮我们管理缓存,使用注解,简化很多操作。
当然使用 Spring Cache 也有优缺点的.
优点
- 使用注解,简化操作
- 缓存管理器,方便多种实现切换缓存源,如Redis,Guava Cache等
- 支持事务, 即事物回滚时,缓存同时自动回滚
缺点
- 不支持TTL,不能为每个 key 设置单独过期时间expires time,
- 针对多线程没有专门的处理,所以当多线程时,是会产生数据不一致性的。(同样,一般有高并发操作的缓存数据,都会特殊处理,而不太使用这种方式)
Spring Cache 快速上手
不想那么多,先快速上个手,再接着详细说一说。
SpringBoot 常规步骤:
- 导入依赖
- 修改配置文件(这一步也可以直接写在第三步)
- 编写xxxxConfig 或者是Enablexxxx
前期准备
这也一样的,另外我这里使用的是 Spring Cache 整合 Redis 做缓存。
<dependency><groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
一般看到是spring-boot-starter开头的依赖,都可以大胆猜测他们是有一个xxxProperties配置类与之对应的。
修改配置文件:
spring:redis:
host: xxxxx
password: xxxx
#指定缓存类型
cache:
type: redis
#指定存活时间(ms)
redis.time-to-live: 86400000
#是否缓存空值,可以防止缓存穿透
redis.cache-null-values: true
与之对应的配置类,大伙可以自己去看看,能配置些啥
另外,在这里进行配置的,在我们的编写xxxConfig类的时候,也同样可以在那里配置。
因为也要配置Redis的配置,就把之前文章里面的东西都粘贴过来了~
/*** @description:
* @author: Ning Zaichun
* @date: 2022年10月22日 23:21
*/
@EnableConfigurationProperties(CacheProperties.class)
@EnableCaching
@Configuration
public class MyRedisConfig {
private final Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer(Object.class);
{
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(objectMapper);
}
/**
* 1.原来的配置类形式
* @ConfigurationProperties(prefix = "spring.cache")
* public class CacheProperties {
* 因为这个并没有放到容器中,所以要让他生效 @EnableConfigurationProperties(CacheProperties.class)
* 因为这个和配置文件已经绑定生效了
* @return
*/
@Bean
RedisCacheConfiguration redisCacheConfiguration(CacheProperties CacheProperties) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
//因为key的序列化默认就是 StringRedisSerializer
// config = config.serializeKeysWith(RedisSerializationContext
// .SerializationPair
// .fromSerializer(new StringRedisSerializer()));
config = config.serializeValuesWith(RedisSerializationContext
.SerializationPair
.fromSerializer(serializer));
CacheProperties.Redis redisProperties = CacheProperties.getRedis();
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
//设置value 值的序列化
redisTemplate.setValueSerializer(serializer);
//key的序列化
redisTemplate.setKeySerializer(new StringRedisSerializer());
// set hash hashkey 值的序列化
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
// set hash value 值的序列化
redisTemplate.setHashValueSerializer(serializer);
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}
}
前期准备结束,直接上手使用~
开始使用
controller-- service--mapper 一路到底,我这里是连接了数据库,只是测试的话,直接在service 的返回结果中存一串字符串即可。
/*** @description:
* @author: Ning Zaichun
* @date: 2022年09月06日 22:16
*/
@RestController
@RequestMapping("/cache")
@RequiredArgsConstructor
public class CacheController {
private final IUseSpringCache useSpringCache;
@GetMapping("/test")
public String getTest() {
return useSpringCache.getTest();
}
@GetMapping("/test2")
public String getTest2() {
return useSpringCache.getTest2();
}
@GetMapping("/test/clear")
public String clearTest() {
useSpringCache.clearTest();
return "clearTest";
}
@GetMapping
public List<MenuEntity> getMenuList() {
return useSpringCache.getMenuList();
}
@GetMapping("/clear")
public String updateMenu() {
MenuEntity menuEntity = new MenuEntity();
menuEntity.setCatId(33L);
menuEntity.setName("其他测试数据");
useSpringCache.updateMenuById(menuEntity);
return "成功清空缓存";
}
} /**
* @description:
* @author: Ning Zaichun
* @date: 2022年09月21日 20:30
*/
public interface IUseSpringCache {
String getTest();
String getTest2();
void clearTest();
List<MenuEntity> getMenuList();
void updateMenuById(MenuEntity menuEntity);
} /**
* @description:
* @author: Ning Zaichun
* @date: 2022年09月21日 20:30
*/
@Service
@RequiredArgsConstructor
public class UseSpringCacheImpl implements IUseSpringCache {
private final MenuMapper menuMapper;
@Cacheable(value = {"menu"}, key = "'getMenuList'")
@Override
public List<MenuEntity> getMenuList() {
System.out.println("查询数据库======");
List<MenuEntity> menuEntityList = menuMapper.selectList(new QueryWrapper<>());
return menuEntityList;
}
/**
* 级联更新所有关联的数据
*
* @param menuEntity
* @CacheEvict:失效模式
* @CachePut:双写模式,需要有返回值 1、同时进行多种缓存操作:@Caching
* 2、指定删除某个分区下的所有数据 @CacheEvict(value = "menu",allEntries = true)
* 3、存储同一类型的数据,都可以指定为同一分区
*/
// @Caching(evict = {
// @CacheEvict(value = "category",key = "'getLevel1Categorys'"),
// @CacheEvict(value = "category",key = "'getCatalogJson'")
// })
@CacheEvict(value = "menu", allEntries = true) //删除某个分区下的所有数据
@Transactional(rollbackFor = Exception.class)
@Override
public void updateMenuById(MenuEntity menuEntity) {
System.out.println("清空缓存======");
menuMapper.updateById(menuEntity);
}
@Cacheable(value = {"test"}, key = "#root.methodName")
@Override
public String getTest() {
System.out.println("测试查询了数据库");
return "我是测试缓存数据";
}
@Cacheable(value = {"test"}, key = "'getTest2'")
@Override
public String getTest2() {
System.out.println("测试查询了数据库2");
return "我是测试缓存数据2";
}
@Caching(evict = {
@CacheEvict(value = "test",key = "'getTest'")
})
@Override
public void clearTest() {
System.out.println("清空了test缓存");
}
}
测试
上面就是简单的使用,上面的注解啥的,马上就开说哈
先讲讲案例中的两个删除缓存的注解
@CacheEvict(value = "menu", allEntries = true)@Caching(evict = {
@CacheEvict(value = "test",key = "'getTest'")
})
两种方式,allEntries = true表示直接清空掉整个分区,
而第二种方式,只会清掉getTest的分区。
在Redis的缓存,它的格式是这样的。
采用第二种方式时,只会清理掉getTest的分区。
变成下面这样:
上面的案例,我只是使用最简单的方式使用了一下 Spring Cache
但其实注解上远不止这么一点东西,接下来慢慢说一说