- 代碼
@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;