缓存-SpringCache
Spring Cache是Spring框架提供的对缓存使用的抽象类,支持多种缓存,比如Redis、EHCache等,集成很方便。同时提供了多种注解来简化缓存的使用,可对方法进行缓存
SpringCache 中的@Cacheable注解
pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
xml配置的属性是在这里封装着
除了在容器内@Bean放了一堆组件,又拿选择器导入了好多缓存的配置,
按照每一种缓存的类型在这里映射,
缓存管理器在自动配的时候,会根据缓存配置的,所有配置的缓存的名字
那相当于在配置文件中配了如果配了spring.cache.cacheNames,将把缓存的区域划分成哪些业务,配上了这些缓存的名字以后
初始化缓存
把每一个缓存拿来遍历,把缓存配置和当前缓存名放在一起,配置都是用默认配置,再用默认配置初始化所有缓存
整个初始化逻辑又在下面,将所有缓存的配置拿来,在initialCaches里面一放相当于哪些缓存都是哪些规则,最终都是在这保存好的
RedisCacheConfiguration在配置缓存规则的时候,比如有序列化机制,是jdk默认的序列化,ttl过期时间,都是从properties里面得到的,每一个缓存件有没有前缀,要不要缓存空数据,以及是不是使用缓存的前缀
redis缓存配置
package com.song.gulimall.product.config;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
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;
/* *
* @program: gulimall
* @description
* @author: swq
* @create: 2021-03-23 12:48
**/
@Configuration
@EnableCaching
// 开启属性配置绑定
@EnableConfigurationProperties({CacheProperties.class})
public class MyCacheConfig {
/* *
* 配置文件中的东西没有用上
*1、原来和配置文件绑定的配置类,是这样子的
* @ConfigurationProperties(prefix = "spring.cache")
* public class CacheProperties
*2、要让他生效,要用这个注解,
* @return
*/
@Bean
public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
// 获取默认配置
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
// 设置 key的序列化方式
config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
// 设置 value的序列化方式 json传输
config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
CacheProperties.Redis redisProperties = cacheProperties.getRedis();
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixKeysWith(redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
}
测试
@Override
@Cacheable({"category"})
public List<CategoryEntity> getCategoryOneLevel() {
return this.baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
}
redis中的数据
缓存-SpringCache-@Cacheable细节设置
* 3、默认行为
* 1)、如果缓存中有,方法不用调用
* 2)、key默认自动生成,缓存名字::SimpleKey [](自动生成的key)
* 3)、缓存的value值,默认使用的是jdk的序列化机制,将序列化后的值存在redis中
* 4)、默认时间ttl=-1
*
* 自定义属性:
* 1)、指定生成的缓存使用的key:key属性指定,使用spel表达式
* SPEL表达式:https://docs.spring.io/spring/docs/5.2.7.RELEASE/spring-framework-reference/integration.html#cache-spel-context
* 2)、指定缓存的数据的存活时间:配置文件中修改ttl,spring.cache.redis.time-to-live=3600000
* 3)、将数据保存为json格式(异构系统比如php可能不兼容)
因为spel动态取值,所有需要额外加''表示字符串
key是自己定义的,必须要带单引号’’
@Cacheable(value = {"catagory"},key = "'Level1Categorys'")
key是方法名
@Cacheable(value = {"catagory"},key = "#root.method.name")
缓存-SpringCache-自定义缓存配置
在redis配置里面,如果是从人家默认的配置的,会从redcacheProperties中拿到redis配置的相关东西,给这里配置上,但是用自己的就不走下面这些步骤了
spring.cache.type=redis
#spring.cache.cache-names=
spring.cache.redis.time-to-live=3600000
#如果使用前缀,就用我们指定的前缀,如果没有就默认使用缓存的名字作为前缀
spring.cache.redis.key-prefix=CACHE_
spring.cache.redis.use-key-prefix=true
# 是否缓存空值。防止缓存穿透
spring.cache.redis.cache-null-values=true
缓存-SpringCache-@CacheEvict
/**
* 级联数据的更新,
* @CacheEvict:失效模式
* @param category
*/
@CacheEvict(value = {"catagory"}, key="'getLevel1Categorys'")
@Transactional(rollbackFor = Exception.class)
@Override
public void updateCascade(CategoryEntity category) {
this.updateById(category);
categoryBrandRelationDao.updateCategory(category.getCatId(), category.getName());
//同时修改缓存中的数据,
//redis.del('catalogJSON'),等待下次查询时更新
}
@Cacheable(value = {"catagory"},key = "#root.method.name")
@Override
public Map<String, List<Catalog2Vo>> getCatalogJson() {
System.out.println(Thread.currentThread().getName() + "查询了数据库");
List<CategoryEntity> selectList = baseMapper.selectList(null);
List<CategoryEntity> category = getParent_cid(selectList, 0L);
//2、封装数据
Map<String, List<Catalog2Vo>> parent_cid = category.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
//1、查到这个一级分类下的所有二级分类
List<CategoryEntity> categoryEntities = getParent_cid(selectList, v.getCatId());
//2、封装上面的结果
List<Catalog2Vo> catalog2Vos = new ArrayList<>();
if (categoryEntities != null && categoryEntities.size() != 0) {
catalog2Vos = categoryEntities.stream().map(l2 -> {
Catalog2Vo catalog2Vo = new Catalog2Vo(
l2.getParentCid().toString(), null, l2.getCatId().toString(), l2.getName());
//1、找当前二级分类的三级分类封装成vo
List<CategoryEntity> level3Catalog = getParent_cid(selectList, l2.getCatId());
List<Catalog2Vo.Catalog3Vo> collectlevel3 = new ArrayList<>();
if (level3Catalog != null && !level3Catalog.isEmpty()) {
//2、封装成指定格式
collectlevel3 = level3Catalog.stream().map(l3 -> {
Catalog2Vo.Catalog3Vo catalog3Vo = new Catalog2Vo.Catalog3Vo(l3.getParentCid().toString(),
l3.getCatId().toString(), l3.getName());
return catalog3Vo;
}).collect(Collectors.toList());
}
catalog2Vo.setCatalog3List(collectlevel3);
return catalog2Vo;
}).collect(Collectors.toList());
}
return catalog2Vos;
}));
return parent_cid;
}
@Caching(evict={
@CacheEvict(value = {"catagory"}, key = "'getLevel1Categorys'"),
@CacheEvict(value = {"catagory"}, key = "'getCatalogJson'")
})
@Transactional(rollbackFor = Exception.class)
@Override
public void updateCascade(CategoryEntity category) {xxxx}
缓存-SpringCache-原理与不足
解决缓存击穿的问题 sync = true
@Cacheable(value = {"catagory"}, key = "#root.method.name", sync = true)
@Override
public List<CategoryEntity> getLevel1Categorys() {
xxxx
}
参考文档地址 :https://blog.csdn.net/qq_37223597/article/details/109265257