@Cacheable
主要是针对方法配置,能根据方法的请求参数对结果进行缓存
@Cacheable(cacheNames = "user:",key = "#id",condition = "#id > 0")
@Override
public Suser selectByPrimaryKey(int id) {
return userDao.selectByPrimaryKey(id);
}
@Cacheable的参数:
cacheNames:缓存的名称,必须至少指定一个
key:缓存的关键字,可以为空,按照spEL表达式编写,如果为空则按照所有参数进行组合定义
condition:使用缓存的条件,可以为空,使用spEl表达式编写,结果为“true”才执行缓存,“false”则不缓存。
@CachePut
在Spring Cache中,在我们使用@Cacheable注解时,我们会先查找在缓存中是否存在该key的缓存,如果没有,会查找数据库数据并添加到缓存中,而我们在使用@CachePut注解时,无论在缓存中是否存在缓存,程序都会执行,并将查到的结果添加指定的缓存中。
//@CachePut也可以标注在类上和方法上。使用@CachePut时我们可以指定的属性跟@Cacheable是一样的。
@CachePut("user")//每次都会执行方法,并将结果存入指定的缓存中
public User find(Integer id) {
return null;
}
@CacheEvict
@CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性类似。即value表示清除操作是发生在哪些Cache上的(对应Cache的名称);key表示需要清除的是哪个key,如未指定则会使用默认策略生成的key;condition表示清除操作发生的条件。
@CacheEvit 的属性:
cachename:缓存的名称,至少指定一个
key:缓存的关键字,可以为空,按照spEL表达式编写,如果为空则按照所有参数进行组合定义
condition:使用缓存的条件,可以为空,使用spEl表达式编写,结果为“true”才执行清空缓存,“false”则不清空缓存。
allEntries:是否清空所有缓存,使用spEl表达式编写,当程序执行时,为true则全部清空缓存,false则不全部清空,缺省为false。
beforeInvocation:是否在程序执行前清空缓存,为true时则在程序执行前清空缓存,为false则不清空,缺省为false。
@Caching
@Caching注解可以让我一个类或方法上定义多个Spring cache相关的注解,其拥有三个属性:cacheable、put和evict,分别用于指定@Cacheable、@CachePut和@CacheEvict。
@Caching(cacheable = @Cacheable("user"), evict = { @CacheEvict("cache2"),
@CacheEvict(value = "cache3", allEntries = true) })
public User find(Integer id) {
return null;
}
1.pom导jar包
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.boot
spring-boot-starter-cache
2.redis配置yml
redis:
#超时时间
timeout: 10000ms
#服务器地址
host: xxx
#服务器端口
port: 6379
#数据库
database: 0
#密码
password: xxx
lettuce:
pool:
#最大连接数,默认8
max-active: 1024
#最大连接阻塞等待时间,默认-1
max-wait: 10000ms
#最大空闲连接
max-idle: 200
#最小空闲连接
min-idle: 5
然后就是整合配置走起
RedisUtil
package com.yqs.utils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisConnectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.concurrent.TimeUnit;
@RequiredArgsConstructor
@Component
@SuppressWarnings({"unchecked", "all"})
@Slf4j
public class RedisUtil {
private final RedisTemplate
然后就是整合Spring Cache
package com.yqs.config.redis;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypevalidator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
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.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;
import org.springframework.data.redis.support.collections.RedisProperties;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.time.Duration;
@Slf4j
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
private static final FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
//主键生成策略 不设置主键时的生成策略 类名+方法名+参数
@Override
@Bean
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();
}
};
}
//缓存管理器。可以管理多个缓存
//只有CacheManger才能扫描到cacheable注解
//spring提供了缓存支持Cache接口,实现了很多个缓存类,其中包括RedisCache。但是我们需要对其进行配置,这里就是配置RedisCache
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheManager cacheManager = RedisCacheManager.RedisCacheManagerBuilder
//Redis链接工厂
.fromConnectionFactory(connectionFactory)
//缓存配置 通用配置 默认存储一小时
.cacheDefaults(getCacheConfigurationWithTtl(Duration.ofHours(2)))
//配置同步修改或删除 put/evict
.transactionAware()
//对于不同的cacheName我们可以设置不同的过期时间
.withCacheConfiguration("article:",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
@SuppressWarnings("all")
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(factory);
// Json序列化配置
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(LaissezFaireSubTypevalidator.instance,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
jackson2JsonRedisSerializer.setObjectMapper(om);
// String 的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
//缓存的异常处理
@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);
}
};
}
}
//重写FastJsonRedisSerialize的实现方式。
class FastJsonRedisSerializer implements RedisSerializer {
private ObjectMapper objectMapper = new ObjectMapper();
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class clazz;
static {
// 全局开启AutoType,这里方便开发,使用全局的方式
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
// 建议使用这种方式,小范围指定白名单
// ParserConfig.getGlobalInstance().addAccept("me.zhengjie.domain");
// key的序列化采用StringRedisSerializer
}
public FastJsonRedisSerializer(Class clazz) {
super();
this.clazz = clazz;
}
//序列化 我们存储时,存储的是json对象,而默认存储的是byte类型的,所以在可视化窗口上显示时,看到的是乱码
@Override
public byte[] serialize(T t) throws SerializationException {
System.out.println("进行序列化");
if (t == null) {
return new byte[0];
}
return JSON.toJSonString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
//反序列化
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return JSON.parseObject(str, clazz);
}
}
使用小案例:
//=================================================前端文章展示=======================================
@Cacheable(cacheNames="article:", key="#layout+#pageNumVO.current")
List queryArticleListByLayout(@NotBlank String layout, @Valid PageNumVO pageNumVO);



