目录
(一)什么是SpringCache?
SpringCache是一个由Spring提供的缓存框架,便于我们用注解的形式操作各种缓存
(二)解决了什么问题?
在开发中我们如果使用redis做缓存通常情况下就是,先查redis看数据是否存在如果没有查数据库然后写入到redis这一套代码大多数场景都要用到,这样就使重复冗余代码出现在项目的各个角落,并且如果我们将这些代码放在业务代码中也不好看,一旦改动我们就需要去业务代码里面做修改,所以SpringCache已注解的形式出现了,让我们轻轻松松完成缓存的操作。
(三)如何使用SpringCache?
1.引入依赖
<!--SpringCache依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--Redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.启动类添加注解开启SpringCache
@Slf4j
@SpringBootApplication
@EnableCaching//开启缓存功能
public class SpringBootApplicaitonMain {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplicaitonMain.class,args);
log.info("项目启动成功...");
}
}
3.配置SpringCache操作的产品为Redis,因为SpringCache还可以操作其他缓存产品如Caffice或本地内存,这里我们指定为Redis,并且设置在使用SpringCache操作缓存时的过期时间(统一)
server:
port: 8080
spring:
redis:
host: 172.17.2.94
port: 6379
password: root@123456
database: 0
cache:
redis:
time-to-live: 1800000 #设置缓存过期时间,可选
如果希望想有更细化的配置可以编写配置类
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.*;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.time.Duration;
@Slf4j
@Configuration
@EnableCaching
@EnableConfigurationProperties(RedisProperties.class)
public class CacheConfig extends CachingConfigurerSupport {
private static final FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
//缓存管理器。可以管理多个缓存
//只有CacheManger才能扫描到cacheable注解
//spring提供了缓存支持Cache接口,实现了很多个缓存类,其中包括RedisCache。但是我们需要对其进行配置,这里就是配置RedisCache
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheManager cacheManager = RedisCacheManager.RedisCacheManagerBuilder
//Redis链接工厂
.fromConnectionFactory(connectionFactory)
//缓存配置 通用配置 默认存储一小时
.cacheDefaults(getCacheConfigurationWithTtl(Duration.ofHours(1)))
//配置同步修改或删除 put/evict
.transactionAware()
//对于不同的cacheName我们可以设置不同的过期时间
// .withCacheConfiguration("app:",getCacheConfigurationWithTtl(Duration.ofHours(5)))
.withCacheConfiguration("user:",getCacheConfigurationWithTtl(Duration.ofHours(2)))
.build();
return cacheManager;
}
//缓存的基本配置对象
private RedisCacheConfiguration getCacheConfigurationWithTtl(Duration duration) {
return RedisCacheConfiguration
.defaultCacheConfig()
//设置key value的序列化方式
// 设置key为String
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// 设置value 为自动转Json的Object
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer))
// 不缓存null
.disableCachingNullValues()
// 设置缓存的过期时间
.entryTtl(duration);
}
//缓存的异常处理
@Bean
@Override
public CacheErrorHandler errorHandler() {
// 异常处理,当Redis发生异常时,打印日志,但是程序正常走
log.info("初始化 -> [{}]", "Redis CacheErrorHandler");
return new CacheErrorHandler() {
@Override
public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
log.error("Redis occur handleCacheGetError:key -> [{}]", key, e);
}
@Override
public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e);
}
@Override
public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e);
}
@Override
public void handleCacheClearError(RuntimeException e, Cache cache) {
log.error("Redis occur handleCacheClearError:", e);
}
};
}
@Override
@Bean("myKeyGenerator")
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuffer sb = new StringBuffer();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
}
4.注解@CachePut这个注解的作用就是在你添加数据时也就是新增时,将你添加的数据放到缓存中,通常用不到因为我们即使不添加到redis中,你经过一次查询也会写到redis里面,所以不用刻意添加。
代码里面的value和key加起来就是你在redis里面的key至于redis里面的value就是你的返回值他会默认添加到redis里面,至于为什么value和key要分开写,你可以这样理解 用户user有很多种数据要缓存有身份信息、账户信息,那么他们都属于用户的数据所以叫value就是userCache但是还需要做区分所以可以在key里面进行区分,其实和redisKey的设计方式一样。
@CachePut(value = "userCache", key = "user.id")
@PostMapping
public User save(User user) {
userService.save(user);
return user;
}
5.注解@CacheEvict这个注解的作用就是删除的意思,当数据库发生变更如新增或者删除数据时,那么我们缓存是不是也要跟着变化,那么我们就可以把缓存删除掉让他为空,这样在查询时就可以从缓存中查询
@CachePut(value = "userCache", key = "user.id")
@PostMapping
public User save(User user) {
userService.save(user);
return user;
}
@CacheEvict还有这种情况就是批量删除或者你想删除这个key下面所有的缓存可以加上allEntries代表setmeallCache下面所有key的数据我都删除
@DeleteMapping
@CacheEvict(value = "setmealCache", allEntries = true)
public R<String> delete(@RequestParam List<Long> ids) {
log.info("ids:{}", ids);
setmealService.deleteByIds(ids);
return R.success(GlobalConstants.FINISHED);
}
6.注解@Cacheable这个注解的作用就是先查缓存如果缓存没有查数据库然后写入到缓存
然后我们是已列表的形式添加数据我们想用ID做区分就可以从result也就是结果中获取ID,unless的意思是不满足这个条件我才执行缓存逻辑,什么意思呢就是你查询的结果result==null这是一个条件而unless就会把他取反当你result不为null时我才执行,与之对应的是condition满足条件我才执行,但是因为@Cacheable执行使用unless才能获取到结果result做判断,所以这里使用unless,添加这个的意思其实就是不想将null的数据添加到缓存
@GetMapping("/list")
@Cacheable(value = "setmealCache", key = "#result.setmeal.categoryId", unless = "result == null && result.size() == 0 ")
public List<Setmeal> list(Setmeal setmeal) {
LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(setmeal.getCategoryId() != null, Setmeal::getCategoryId, setmeal.getCategoryId());
queryWrapper.eq(setmeal.getStatus() != null, Setmeal::getStatus, setmeal.getStatus());
queryWrapper.orderByDesc(Setmeal::getUpdateTime);
List<Setmeal> list = setmealService.list(queryWrapper);
return list;
}