- 代码
@Cacheable(cacheNames = "article", cacheManager = "cacheManager", keyGenerator = "keyGenerator", condition = "#id!=null && #id!=''", unless = "#id==1") @Override public Article byId(String id) { log.info("查找id为{}的文章", id); //调用dao层 return articleDao.byId(id); }
- 结果
- 原因
- 查看数据是在何时被存入缓存中。
- 找到缓存自动配置类
CacheAutoConfiguration
找到Redis的自动配置类
缓存管理器CacheManager是缓存的抽象,RedisCacheManager是对抽象的实现
Redis缓存管理器
- 进入
RedisCacheManager
类 - 根据继承关系得知,一般通过的方法都在AbstractXXX类中
- 进入
AbstractCacheManager
类
Cache
类的角色与作用
debug类的调用关系可达:
进入serializeCacheValue(cacheValue)方法
cacheConfig.getValueSerializationPair()
返回的是 RedisCacheConfiguration
类下的SerializationPair<Object> valueSerializationPair
,并且是通过构造方法注入进来的
- 那么把这个序列化类改成我们自定的应该就可以了
- 回到向容器中添加这个Bean的地方,可发现:
JDK的序列化方式
- 使用fastjson实现自定义的序列化方式-并将JDK的序列化方式改为自定义的序列化方式-需要自定义我们自己的CacheManager
package com.lazy.cache.redis; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.SerializationException; /** * @author futao * Created on 2019/10/24. */ public class FastJsonRedisSerializer4CacheManager<T> implements RedisSerializer<T> { private final FastJsonRedisSerializer<T> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(); @Override public byte[] serialize(T t) throws SerializationException { return fastJsonRedisSerializer.serialize(t); } @Override public T deserialize(byte[] bytes) throws SerializationException { return fastJsonRedisSerializer.deserialize(bytes); } }
package com.lazy.cache.redis; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.SerializationException; import java.nio.charset.StandardCharsets; /** * 自定义Redis序列化,对于redisTemplate.opsForValue.set()有效,对注解@Cache无效,因为@Cache注解使用的是RedisTemplate<Object.Object>, * --可以自定义RedisCacheManager,并将redisTemplate设置成自定义的序列化工具,然后再@Cache()中使用这个自定义的RedisCacheManager * * @author futao * Created on 2019-03-22. */ public class FastJsonRedisSerializer<T> implements RedisSerializer<T> { /** * 仅仅用作识别JSON.parseObject(text,class)方法 */ private Class<T> clazz = null; protected static final SerializerFeature[] SERIALIZER_FEATURES = new SerializerFeature[]{ SerializerFeature.PrettyFormat , SerializerFeature.SkipTransientField // , SerializerFeature.WriteEnumUsingName // , SerializerFeature.WriteDateUseDateFormat , SerializerFeature.WriteNullStringAsEmpty , SerializerFeature.WriteNullListAsEmpty , SerializerFeature.WriteMapNullValue // 【重点】序列化的时候必须需要带上Class类型,否则反序列化的时候无法知道Class类型 , SerializerFeature.WriteClassName }; /** * 序列化 * * @param t 数据 * @return * @throws SerializationException */ @Override public byte[] serialize(T t) throws SerializationException { return t == null ? null : JSON.toJSONString(t, SERIALIZER_FEATURES).getBytes(StandardCharsets.UTF_8); } /** * 反序列化 * clazz为null也可以反序列化成功是因为对象在序列化的时候保存了对象的class * * @param bytes 字节数组 * @return * @throws SerializationException */ @Override public T deserialize(byte[] bytes) throws SerializationException { return bytes == null ? null : JSON.parseObject(new String(bytes, StandardCharsets.UTF_8), clazz); } }
package com.lazy.cache.redis; import com.alibaba.fastjson.parser.ParserConfig; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration; import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizers; import org.springframework.boot.autoconfigure.cache.CacheProperties; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; import org.springframework.core.annotation.Order; import org.springframework.core.io.ResourceLoader; 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.RedisSerializationContext; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.util.LinkedHashSet; import java.util.List; /** * @author futao * Created on 2019/10/24. */ @Configuration @Order @AutoConfigureAfter({CacheAutoConfiguration.class}) @Import({CacheAutoConfiguration.class}) public class RedisConfig { private final CacheProperties cacheProperties; private final CacheManagerCustomizers customizerInvoker; private final RedisCacheConfiguration redisCacheConfiguration; public RedisConfig(CacheProperties cacheProperties, CacheManagerCustomizers customizerInvoker, ObjectProvider<RedisCacheConfiguration> redisCacheConfiguration) { this.cacheProperties = cacheProperties; this.customizerInvoker = customizerInvoker; this.redisCacheConfiguration = redisCacheConfiguration.getIfAvailable(); } /** * 自定义序列化 * 这里的FastJsonRedisSerializer引用的自己定义的 * 不自定义的话redisTemplate会乱码 */ @Primary @Bean public <T> RedisTemplate<String, T> redisTemplate(RedisConnectionFactory factory) { //redis反序列化 开启fastJson反序列化的autoType ParserConfig.getGlobalInstance().setAutoTypeSupport(true); RedisTemplate<String, T> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(factory); FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer<T>(); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); redisTemplate.setDefaultSerializer(fastJsonRedisSerializer); redisTemplate.setKeySerializer(stringRedisSerializer); redisTemplate.setHashKeySerializer(stringRedisSerializer); redisTemplate.setValueSerializer(fastJsonRedisSerializer); redisTemplate.setHashValueSerializer(fastJsonRedisSerializer); return redisTemplate; } @Primary @Bean public KeyGenerator keyGenerator() { return (target, method, params) -> { StringBuilder sb = new StringBuilder(); sb .append(target.getClass().getSimpleName()) .append(":") .append(method.getName()); for (Object param : params) { sb .append(":") .append(param); } return sb.toString(); }; } @Primary @Bean public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) { RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager .builder(redisConnectionFactory) .cacheDefaults(determineConfiguration(resourceLoader.getClassLoader())); List<String> cacheNames = this.cacheProperties.getCacheNames(); if (!cacheNames.isEmpty()) { builder.initialCacheNames(new LinkedHashSet<>(cacheNames)); } return this.customizerInvoker.customize(builder.build()); } /** * 读取redisCache配置 * * @param classLoader * @return */ private RedisCacheConfiguration determineConfiguration( ClassLoader classLoader) { if (this.redisCacheConfiguration != null) { return this.redisCacheConfiguration; } CacheProperties.Redis redisProperties = this.cacheProperties.getRedis(); RedisCacheConfiguration config = RedisCacheConfiguration .defaultCacheConfig(); //指定采用的序列化工具 config = config.serializeValuesWith(RedisSerializationContext.SerializationPair .fromSerializer(new FastJsonRedisSerializer4CacheManager<>())); 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; } }
- 再debug,可发现程序已经进入了我们自定义的序列化方法
再查看缓存
- 乱码问题解决
在项目中使用RedisTemplate<String,T>
- 自定义序列化类
package com.lazy.cache.redis; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.SerializationException; import java.nio.charset.StandardCharsets; /** * @author futao * Created on 2019-03-22. */ public class FastJsonRedisSerializer<T> implements RedisSerializer<T> { /** * 仅仅用作识别JSON.parseObject(text,class)方法 */ private Class<T> clazz = null; protected static final SerializerFeature[] SERIALIZER_FEATURES = new SerializerFeature[]{ SerializerFeature.PrettyFormat , SerializerFeature.SkipTransientField // , SerializerFeature.WriteEnumUsingName // , SerializerFeature.WriteDateUseDateFormat , SerializerFeature.WriteNullStringAsEmpty , SerializerFeature.WriteNullListAsEmpty , SerializerFeature.WriteMapNullValue // 【重点】序列化的时候必须需要带上Class类型,否则反序列化的时候无法知道Class类型 , SerializerFeature.WriteClassName }; /** * 序列化 * * @param t 数据 * @return * @throws SerializationException */ @Override public byte[] serialize(T t) throws SerializationException { return t == null ? null : JSON.toJSONString(t, SERIALIZER_FEATURES).getBytes(StandardCharsets.UTF_8); } /** * 反序列化 * clazz为null也可以反序列化成功是因为对象在序列化的时候保存了对象的class * * @param bytes 字节数组 * @return * @throws SerializationException */ @Override public T deserialize(byte[] bytes) throws SerializationException { return bytes == null ? null : JSON.parseObject(new String(bytes, StandardCharsets.UTF_8), clazz); } }
- 定义
RedisTemplate<String,T>
Bean
/** * 自定义序列化 * 这里的FastJsonRedisSerializer引用的自己定义的 */ @Primary @Bean public <T> RedisTemplate<String, T> redisTemplate(RedisConnectionFactory factory) { //redis反序列化 开启fastJson反序列化的autoType ParserConfig.getGlobalInstance().setAutoTypeSupport(true); RedisTemplate<String, T> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(factory); FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer<T>(); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); redisTemplate.setDefaultSerializer(fastJsonRedisSerializer); redisTemplate.setKeySerializer(stringRedisSerializer); redisTemplate.setHashKeySerializer(stringRedisSerializer); redisTemplate.setValueSerializer(fastJsonRedisSerializer); redisTemplate.setHashValueSerializer(fastJsonRedisSerializer); return redisTemplate; }
- 使用
@Autowired private RedisTemplate<String, User> redisTemplate;