- 代码
@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
,并且是通过构造方法注入进来的
那么把这个序列化类改成我们自定的应该就可以了
-
回到向容器中添加这个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 implements RedisSerializer {
private final FastJsonRedisSerializer 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,
* --可以自定义RedisCacheManager,并将redisTemplate设置成自定义的序列化工具,然后再@Cache()中使用这个自定义的RedisCacheManager
*
* @author futao
* Created on 2019-03-22.
*/
public class FastJsonRedisSerializer implements RedisSerializer {
/**
* 仅仅用作识别JSON.parseObject(text,class)方法
*/
private Class 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) {
this.cacheProperties = cacheProperties;
this.customizerInvoker = customizerInvoker;
this.redisCacheConfiguration = redisCacheConfiguration.getIfAvailable();
}
/**
* 自定义序列化
* 这里的FastJsonRedisSerializer引用的自己定义的
* 不自定义的话redisTemplate会乱码
*/
@Primary
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
//redis反序列化 开启fastJson反序列化的autoType
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
RedisTemplate redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer();
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 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
- 自定义序列化类
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 implements RedisSerializer {
/**
* 仅仅用作识别JSON.parseObject(text,class)方法
*/
private Class 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
Bean
/**
* 自定义序列化
* 这里的FastJsonRedisSerializer引用的自己定义的
*/
@Primary
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
//redis反序列化 开启fastJson反序列化的autoType
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
RedisTemplate redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer();
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setDefaultSerializer(fastJsonRedisSerializer);
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(fastJsonRedisSerializer);
redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
return redisTemplate;
}
- 使用
@Autowired
private RedisTemplate redisTemplate;
我在这里等你: