1、为什么需要缓存
:::tips 前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时从数据库中取,数据库取到更新缓存,并返回结果,数据库也没取到,那直接返回空结果 :::
2、SpringCache概述
:::tips 使用Spring Cache的好处:
- 提供基本的Cache抽象,方便切换各种底层Cache;
- 通过注解Cache可以实现类似于事务一样,缓存逻辑透明的应用到我们的业务代码上,且只需要更少的代码就可以完成;
- 提供事务回滚时也自动回滚缓存;
- 支持比较复杂的缓存逻辑;
:::
2.1、SpringCache概述及核心配置
它利用了AOP(将缓存逻辑与服务逻辑解耦)2.2 SpringCache环境准备
<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>
```java /**#缓存核心配置 spring: redis: host: xxxxx # Redis服务器地址 database: 1 # Redis数据库索引(默认为0) port: 6379 # Redis服务器连接端口 # password: ld123456 # Redis服务器连接密码(默认为空)
- @author itheima
code 自定义redis序列化配置类 / @Configuration public class RedisCacheConfig { /*
- 配置redis序列化
- 1、数据将以json格式保存
- 2、将默认的jdk序列化规则改为redis序列化规则
- 配置redisTemplate 代替默认配置
- @param redisConnectionFactory RedisConnectionFactory
@return RedisTemplate */ @Bean(“redisTemplate”) public RedisTemplate
redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper mapper = new ObjectMapper(); //禁止将Date转Timestameps mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); //属性值为null的属性不做序列化处理 mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); //设置暴力访问任意方法 mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //仅仅序列化对象的属性,且属性不可为final修饰 mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); serializer.setObjectMapper(mapper); //设置value序列化方式 template.setValueSerializer(serializer); //使用StringRedisSerializer来序列化和反序列化redis的key值 template.setKeySerializer(new StringRedisSerializer()); template.afterPropertiesSet(); return template; } /**
- 配置 cacheManager 代替默认的cacheManager (缓存管理器)
- @param factory RedisConnectionFactory
@return CacheManager */ @Bean public CacheManager cacheManager(RedisConnectionFactory factory) { RedisSerializer
redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //仅仅序列化对象的属性,且属性不可为final修饰 objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); serializer.setObjectMapper(objectMapper); // 配置key value序列化 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer)) //关闭控制存储 .disableCachingNullValues() //修改前缀与key的间隔符号,默认是:: .computePrefixWith(cacheName->cacheName+":");
//设置特有的Redis配置 Map
cacheConfigurations = new HashMap<>(); //定制化的Cache 设置过期时间 eg:以role:开头的缓存存活时间为10s cacheConfigurations.put(“role”,customRedisCacheConfiguration(config,Duration.ofSeconds(10))); cacheConfigurations.put(“stock”,customRedisCacheConfiguration(config,Duration.ofSeconds(3000))); cacheConfigurations.put(“market”,customRedisCacheConfiguration(config,Duration.ofSeconds(300))); //构建redis缓存管理器 RedisCacheManager cacheManager = RedisCacheManager.builder(factory) //Cache事务支持 .transactionAware() .withInitialCacheConfigurations(cacheConfigurations) .cacheDefaults(config) .build();
//设置过期时间 return cacheManager; }
/**
- 设置RedisConfiguration配置
- @param config
- @param ttl
@return */ public RedisCacheConfiguration customRedisCacheConfiguration(RedisCacheConfiguration config, Duration ttl) { //设置缓存缺省超时时间 return config.entryTtl(ttl); } }
```java /** * @author itheima * code 自定义KeyGenerator生成策略 */ @Configuration public class CacheConfig { /** * 可通过引用myKeyGenerator使用公共的key生成器 * @return */ @Bean("myKeyGenerator") public KeyGenerator keyGenerator(){ return (o,method,objects)->method.getName()+ "["+Arrays.asList(objects).toString()+"]"; } }
2.3 SpringCache注解详解
【1】@Cacheable注解
```java @Service @CacheConfig(cacheNames = “role”) public class RoleServiceImpl implements IRoleService {/ * .@Cacheable:
- 1、方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取;
- (CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建。
- 2、去Cache中查找缓存的内容,使用一个key,默认就是方法的参数;
- key是按照某种策略生成的;默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key;
- SimpleKeyGenerator生成key的默认策略;
- 如果没有参数;key=new SimpleKey();
- 如果有一个参数:key=参数的值
- 如果有多个参数:key=new SimpleKey(params);
- 3、没有查到缓存就调用目标方法;
- 4、将目标方法返回的结果,放进缓存中
- 核心:
- 1)、使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】组件
- 2)、key使用keyGenerator生成的,默认是SimpleKeyGenerator
- 几个属性:
- a)cacheNames/value:指定缓存组件的名字
- cacheNames = {“role”}可以使用多个参数,是数组的形式,可以指定多个缓存
- b)key:缓存数据使用的key,可以用他来指定。默认是使用方法参数的值
- 编写SpEl: #id #a0,#po,#argrs[0] “0”代表参数的索引
result 方法执行后的返回值
root.methodName 方法名
- key = “#root.methodName+’[‘+#id+’]’”
- c)keyGenerator: key的生成器、可以自己指定key的生成器的组件Id
- key/keyGenerator 二选一
- keyGenerator = “myKeyGenerator”
- d)cacheManager:指定缓存管理器或者cacheResolver:获取解析器
- cacheManager/cacheResolver 二选一
- e)condition:指定符合缓存的条件
- condition = “#id>0 and #root.methodName eq ‘aaa’” 可以多条件判断
- f)unless: 否定缓存,当unless的条件为true,方法结果不会被缓存,可以获取结果进行判断
- unless = “#result==null”,结果为null,就不缓存
- g)sync:是否使用异步模式
- 默认false 同步
- 为true时,unless不支持 */ @Autowired private RoleMapper roleMapper;
@Override @Cacheable(key = “#id”,condition = “#id>0”,unless = “#result==null”) public Role findById(Integer id) { return roleMapper.selectByPrimaryKey(id); } // @Cacheable(keyGenerator = “myKeyGenerator”,key = “findAllRole”) @Cacheable(key = “#root.method.getName()”) @Override public CommonResult findAllRole() { List
roleList = roleMapper.findAll(); return CommonResult.success(roleList); }
}
<a name="kq6VW"></a>
#### 【2】@CacheEvict注解
@CacheEvict:删除缓存的注解,这对删除旧的数据和无用的数据是非常有用的。这里还多了一个参数(allEntries),设置allEntries=true时,可以对整个条目进行批量删除<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/29309090/1655811062421-2118160a-569f-4595-a232-92e5eca23f32.png#clientId=u37ccbb00-40dd-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=458&id=u8a987537&margin=%5Bobject%20Object%5D&name=image.png&originHeight=572&originWidth=1097&originalType=binary&ratio=1&rotation=0&showTitle=false&size=420984&status=done&style=none&taskId=u90041fe8-35c6-45ab-82b7-b0b00521641&title=&width=877.6)
```java
/**
* .@CacheEvict 缓存清除
* key:指定要清除的数据
* allEntries:指定清除这个缓存库的所有数据,默认为false
* beforeInvocation:在执行方法之前清除,默认为false,在方法之后执行
*/
@Override
@CacheEvict(/*value = "role",*/key = "#id")
public Integer delete(Integer id) {
return roleMapper.deleteByPrimaryKey(id);
}
【3】@CachePut注解
@CachePut:当需要更新缓存而不干扰方法的运行时 ,可以使用该注解。也就是说,始终执行该方法,并将结果放入缓存
/**
* .@CachePut既调用方法、又更新数据,达到同步更新缓存
* <p>
* 运行时机:
* 1、先调用目标方法
* 2、将目标方法的结果缓存起来
*
* 条件:存取Id的key要保持一致
* key = "#role.id" 传入员工的Id
* key = "#result.id" 使用返回员工的Id
* 注意: @Cacheable不能使用#result
* 因为 @Cacheable在目标方法执行之前需要得到这个key,所以不能用#result
*/
@Override
@CachePut(key = "#result.id")
public Role update(Role role) {
roleMapper.updateByPrimaryKey(role);
return role;
}
【4】@Caching注解
在使用缓存的时候,有可能会同时进行更新和删除,会出现同时使用多个注解的情况.而@Caching可以实现
:::tips
//添加user缓存的同时,移除userPage的缓存
@Caching(put =@CachePut(value = “user”,key =”#userVo.id”),
evict = @CacheEvict(value = “userPage”,allEntries = true))
:::
/**
* .@Caching 定义复杂缓存规则
*/
@Override
@Caching(
cacheable = {
@Cacheable(key = "#role.roleName")
},
put = {
@CachePut(key = "#role.id"),
@CachePut(key = "#role.roleCode")
}
)
public CommonResult add(Role role) {
role.setId(null);
try {
roleMapper.insert(role);
} catch (Exception e) {
return CommonResult.failed();
}
return CommonResult.success(role.getId());
}
优雅使用SpringCache
1、缓存层选择
把缓存放到单独一个层中,face处理缓存