<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
dependency>
思路:
原理分析:
当引入了redis的starter依赖后,
org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration就会起作用,并创建出RedisCacheManager,再由RedisCacheManager创建出RedisCache;
默认的org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration在发现已经有了CacheManager后,它就不会再生效,于是默认的ConcurrentMapCacheManager也不会被创建,ConcurrentMapCache也不会被创建。
这样缓存就由默认的ConcurrentMapCache切换成为了RedisCache。
于是当我们使用@Cacheable 、@CacheEvit 、 @CachePut时,就会将数据缓存在redis中。
但是直接缓存的话,数据在redis中存储的不是JSON格式,因为默认的redisTemplate使用的是JDK序列化机制,是以二进制保存的数据,不利于图形化展示。
因此我们还要对redis进行自定义序列化设置。
package com.haocheng.study.util.redis;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
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.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Redis配置类
* @Author: haocheng
* @Date: 2019-10-17 16:44
*/
@Configuration
//开启缓存
@EnableCaching
public class RedisConfig{
/**
* 自定义redisTemplate
* @param factory
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
//使用Fastjson2JsonRedisSerializer来序列化和反序列化
FastJson2JsonRedisSerializer fastJson2JsonRedisSerializer = new FastJson2JsonRedisSerializer(Object.class);
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
//序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
//序列化和反序列化redis的value值
template.setValueSerializer(fastJson2JsonRedisSerializer);
template.setHashValueSerializer(fastJson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
/**
* 也可以使用jackson实现json序列化
* @return
*/
// @Bean
// public RedisSerializer
// //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
// Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
//
// ObjectMapper mapper = new ObjectMapper();
// mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
// serializer.setObjectMapper(mapper);
// return serializer;
// }
/**
* 自定义缓存管理器
* @param redisConnectionFactory
* @return
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
//使用Fastjson2JsonRedisSerializer来序列化和反序列化redis的value值
FastJson2JsonRedisSerializer fastJson2JsonRedisSerializer = new FastJson2JsonRedisSerializer(Object.class);
// 生成一个默认配置,通过config对象即可对缓存进行自定义配置
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
// 设置缓存的默认过期时间,也是使用Duration设置
config = config.entryTtl(Duration.ofMinutes(1))
// 设置 key为string序列化
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// 设置value为json序列化
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJson2JsonRedisSerializer))
// 不缓存空值
.disableCachingNullValues();
// 设置一个初始化的缓存空间set集合
Set<String> cacheNames = new HashSet<>();
cacheNames.add("cache1");
cacheNames.add("cache2");
// 对每个缓存空间应用不同的配置
Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
configMap.put("cache1", config);
configMap.put("cache2", config.entryTtl(Duration.ofSeconds(120)));
// 使用自定义的缓存配置初始化一个cacheManager
RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory)
// 一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
.initialCacheNames(cacheNames)
.withInitialCacheConfigurations(configMap)
.build();
return cacheManager;
}
}
注:此处采用了fastjson实现序列化
package com.haocheng.study.util.redis;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import java.nio.charset.Charset;
/**
* 使用FastJson序列化和反序列化
*
* @Author: haocheng
* @Date: 2019-10-18 14:36
*/
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> {
private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
public FastJson2JsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}
static {
// 使用fastjson的时候:序列化时将class信息写入,反解析的时候,
// fastjson默认情况下会开启autoType的检查,相当于一个白名单检查,
// 如果序列化信息中的类路径不在autoType中,
// 反解析就会报com.alibaba.fastjson.JSONException: autoType is not support的异常
// ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
ParserConfig.getGlobalInstance().addAccept("com.haocheng.study.model.");
}
@Override
public byte[] serialize(T t) throws SerializationException {
if (null == t) {
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (null == bytes || bytes.length <= 0) {
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return (T) JSON.parseObject(str, clazz);
}
}
直接在service上使用即可
//@Cacheable缓存key为name的数据到缓存cache2中
@Cacheable(value = "cache2", key = "'#param'", unless="#result == null")
public void getDept(String name){
System.out.println("添加数据到缓存中" + name + ",只显示一次");
}
// @CacheEvict从缓存cache2中删除key为name的数据
@CacheEvict(value = "cache2", key = "#param")
public void removeDept(String name) {
System.out.println("删除数据" + name + ",同时清除对应的缓存");
}
1.@Cacheable: 作用是主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
主要参数说明:
eg:@Cacheable(value=”cache”) 或者 @Cacheable(value={”cache1”,”cache2”}。
如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合
例如:@Cacheable(value=”testcache”,key=”#userName”)。
示例:
@Cacheable(value = "my-redis-cache1" ,key = "'book'+#book_id" ,condition = "#book_id>8")
public Book selectByPrimaryKey(Long book_id) {
return bookMapper.selectByPrimaryKey(book_id);
}
2.@CachePut: 作用是主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实查询
方法的调用
主要参数说明:
参数配置和@Cacheable一样。
@CachePut(value = "my-redis-cache2") //只存不取
public Book selectByPrimaryKey(Long book_id) {
return bookMapper.selectByPrimaryKey(book_id);
}
3.@CacheEvict: 作用是主要针对方法配置,能够根据一定的条件对缓存进行清空
主要参数说明:
如果指定为 true,则方法调用后将立即清空所有缓存,
eg:@CachEvict(value=”testcache”,allEntries=true)。
@CacheEvict(value = "my-redis-cache2" ,allEntries = true)
public void clean(){
System.out.println("清理my-redis-cache2缓存..........");
}
beforeInvocation :是否在方法执行前就清空,缺省为 false,
如果指定为 true,则在方法还没有执行的时候就清空缓存,
缺省情况下,如果方法执行抛出异常,则不会清空缓存,
eg: @CachEvict(value=”testcache”,beforeInvocation=true)。