CacheManager基本配置

  请参考博文:springboot2.0 redis EnableCaching的配置和使用

RedisCacheManager构造函数

/*** Construct a {@link RedisCacheManager}.* * @param redisOperations*/
@SuppressWarnings("rawtypes")
public RedisCacheManager(RedisOperations redisOperations) {this(redisOperations, Collections.<String> emptyList());
}/*** Construct a static {@link RedisCacheManager}, managing caches for the specified cache names only.* * @param redisOperations* @param cacheNames* @since 1.2*/
@SuppressWarnings("rawtypes")
public RedisCacheManager(RedisOperations redisOperations, Collection<String> cacheNames) {this.redisOperations = redisOperations;setCacheNames(cacheNames);
}

  RedisCacheManager需要一个 RedisOperations实例,一般是RedisTemplate。还有一个不必须的缓存名称集合参数。

protected RedisCache createCache(String cacheName) {long expiration = computeExpiration(cacheName);return new RedisCache(cacheName, (usePrefix ? cachePrefix.prefix(cacheName) : null), redisOperations, expiration);
}

  在创建缓存时,通过RedisCache的构造函数传入 redisOperations(即RedisTemplate实例)。

设置全局通用的序列化器

  GenericJackson2JsonRedisSerializer

import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
User user = new User();
user.setName("hjzgg");
user.setAge(26);System.out.println(serializer.deserialize(serializer.serialize(user)));public static class User {private String name;private Integer age;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return Objects.toStringHelper(this).add("name", name).add("age", age).toString();}
}

  调试发现,序列化内容加入了对象的类型信息,如下。

  

  查看GenericJackson2JsonRedisSerializer构造函数,序列化和反序列化的实现是通过Jackson的ObjectMapper完成的。并开启了默认类型的配置。

/*** Creates {@link GenericJackson2JsonRedisSerializer} and configures {@link ObjectMapper} for default typing using the* given {@literal name}. In case of an {@literal empty} or {@literal null} String the default* {@link JsonTypeInfo.Id#CLASS} will be used.* * @param classPropertyTypeName Name of the JSON property holding type information. Can be {@literal null}.*/
public GenericJackson2JsonRedisSerializer(String classPropertyTypeName) {this(new ObjectMapper());if (StringUtils.hasText(classPropertyTypeName)) {mapper.enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, classPropertyTypeName);} else {mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);}
}

  Protostuff序列化和反序列化

import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
import org.springframework.data.redis.serializer.RedisSerializer;public class ProtostuffRedisSerializer implements RedisSerializer<Object> {private static final Schema<ObjectWrapper> schema = RuntimeSchema.getSchema(ObjectWrapper.class);public ProtostuffRedisSerializer() {}public byte[] serialize(Object object) {if (object == null) {return new byte[0];} else {LinkedBuffer buffer = LinkedBuffer.allocate(512);byte[] var3;try {var3 = ProtostuffIOUtil.toByteArray(new ObjectWrapper(object), schema, buffer);} finally {buffer.clear();}return var3;}}public Object deserialize(byte[] bytes) {if (bytes != null && bytes.length != 0) {try {ObjectWrapper objectWrapper = new ObjectWrapper();ProtostuffIOUtil.mergeFrom(bytes, objectWrapper, schema);return objectWrapper.getObject();} catch (Exception var3) {throw new RuntimeException(var3.getMessage(), var3);}} else {return null;}}
}public class ObjectWrapper {private Object object;public ObjectWrapper(Object object) {this.object = object;}public ObjectWrapper() {}public Object getObject() {return this.object;}public void setObject(Object object) {this.object = object;}
}

  上面通过Protostuff自定义了一个序列化和反序列化的工具,测试代码如下。

    ProtostuffRedisSerializer serializer = new ProtostuffRedisSerializer();Person person = new Person();person.setName("hjzgg");person.setAge(26);System.out.println(serializer.deserialize(serializer.serialize(person)));
}public static class Person {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}

  调试发现,序列化内容加入了对象的类型信息,如下。

  

  JdkSerializationRedisSerializer

JdkSerializationRedisSerializer serializer = new JdkSerializationRedisSerializer();
User user = new User();
user.setName("hjzgg");
user.setAge(26);System.out.println(serializer.deserialize(serializer.serialize(user)));public static class User implements Serializable {private String name;private Integer age;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return Objects.toStringHelper(this).add("name", name).add("age", age).toString();}
}

  JdkSerializationRedisSerializer构造函数如下,序列转换器和反序列转换器。

/*** Creates a new {@link JdkSerializationRedisSerializer} using the default class loader.*/
public JdkSerializationRedisSerializer() {this(new SerializingConverter(), new DeserializingConverter());
}

  发现JdkSerializationRedisSerializer内部使用的是我们最熟悉的ObjectInputStream和ObjectOutputStream。

  

  

  调试发现,序列化内容加入了对象的类型信息,如下。

  

  要缓存的 Java 对象必须实现 Serializable 接口,因为 Spring 会将对象先序列化再存入 Redis,比如本文中的 User 类,如果不实现 Serializable 的话将会遇到类似这种错误:nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.XXX.User]]。

不同cache设置不同序列化器

  Jackson2JsonRedisSerializer

import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
Jackson2JsonRedisSerializer<?> serializer1 = new Jackson2JsonRedisSerializer<>(JacksonHelper.genJavaType(User.class));
System.out.println(serializer1.deserialize(serializer1.serialize(user)));public static class User {private String name;private Integer age;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return Objects.toStringHelper(this).add("name", name).add("age", age).toString();}
}

  Jackson2JsonRedisSerializer内部序列化过程也是通过Jackson ObjectMapper来完成的,但是序列化内容不包含对象类型信息,如下。

  

  所以,在使用Jackson2JsonRedisSerializer的时候需要指定当前cache存储的对象类型。

  自定义RedisCacheManager

  实现不同RedisCache对应不同的RedisTemplate(即对应不同的序列化器)

static class CustomRedisCacheManager extends RedisCacheManager {private Map<String, RedisCache> redisCaches = Maps.newConcurrentMap();public static final String CACHE_NAME_DEFAULT = "DEFAULT_CACHE";public CustomRedisCacheManager(Map<String, CustomRedisConfiguration> configurations) {super(configurations.get(CACHE_NAME_DEFAULT).getRedisTemplate(), configurations.keySet());configurations.keySet().stream().forEach(cacheName -> redisCaches.put(cacheName, new RedisCache(cacheName, null, configurations.get(cacheName).getRedisTemplate(), configurations.get(cacheName).duration.getSeconds())));}@Overridepublic Cache getCache(String cacheName) {return redisCaches.get(cacheName);}
}

  RedisCacheManager 通过加载自定义配置实现类RedisCacheConfigurationProvider获取不同RedisCache的配置

@Bean
@Primary
public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate, ObjectProvider<RedisCacheConfigurationProvider> provider) {Map<String, CustomRedisConfiguration> configurations = Maps.newHashMap();configurations.put(CustomRedisCacheManager.CACHE_NAME_DEFAULT, new CustomRedisConfiguration(redisTemplate, Duration.ofMinutes(20)));RedisCacheConfigurationProvider configurationProvider = provider.getIfAvailable();if (!Objects.isNull(configurationProvider)) {configurations.putAll(configurationProvider.resolve(redisTemplate.getConnectionFactory()));}RedisCacheManager cacheManager = new CustomRedisCacheManager(configurations);return cacheManager;
}

  RedisCache自定义配置提供者抽象类,根据不同的缓存类型设置不同的序列化器

public static abstract class RedisCacheConfigurationProvider {// key = 缓存名称, value = 缓存时间 和 缓存类型protected Map<String, Pair<Duration, JavaType>> configs;protected abstract void initConfigs();public Map<String, CustomRedisConfiguration> resolve(RedisConnectionFactory connectionFactory) {initConfigs();Assert.notEmpty(configs, "RedisCacheConfigurationProvider 配置不能为空...");Map<String, CustomRedisConfiguration> result = Maps.newHashMap();configs.forEach((cacheName, pair) -> {RedisTemplate<?, ?> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(connectionFactory);redisTemplate.setKeySerializer(new JdkSerializationRedisSerializer());redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(pair.getValue()));redisTemplate.afterPropertiesSet();result.put(cacheName, new CustomRedisConfiguration(redisTemplate, pair.getKey()));});return result;}
}

  用户根据缓存名称设置不同的存储类型

@Component
public class CouponRedisCacheConfigurationProvider extends RedisCacheConfig.RedisCacheConfigurationProvider {@Overrideprotected void initConfigs() {this.configs = Maps.newHashMap();this.configs.put(CouponConstants.COUPON_ALL_CACHE, new Pair<>(Duration.ofHours(12), JacksonHelper.genMapType(HashMap.class, String.class, Coupon.class)));this.configs.put(CouponConstants.COUPON_GOOD_CACHE, new Pair<>(Duration.ofHours(12), JacksonHelper.genCollectionType(List.class, String.class)));this.configs.put(CouponConstants.COUPON_HANDLE_TELEPHONE_STATUS_CACHE, new Pair<>(Duration.ofHours(1), JacksonHelper.genCollectionType(List.class, CouponHandle.class)));this.configs.put(CouponConstants.COUPON_HANDLE_TELEPHONE_GOOD_CACHE, new Pair<>(Duration.ofHours(1), JacksonHelper.genJavaType(CompositeCouponHandle.class)));}
}

  CompositeCacheManager

  复合CacheManager实现了给定的委托CacheManager实例集合。允许NoOpCacheManager自动添加到列表末尾,以便在没有后备存储的情况下处理缓存声明。否则,任何自定义CacheManager也可以扮演最后一个委托的角色,懒惰地为任何请求的名称创建缓存区域。注意:如果复合管理器委托的常规CacheManagers需要从getCache(String)返回null,如果它们不知道指定的缓存名称,则允许迭代到下一个委托。但是,大多数CacheManager实现都会在请求时回退到命名缓存的延迟创建;查看具有固定缓存名称的“静态”模式的特定配置详细信息(如果有)。

  通过CompositeCacheManager 可以配置过个CacheManager,每个CacheManager可以配置不同的序列化器。

public class CompositeCacheManager implements CacheManager, InitializingBean {private final List<CacheManager> cacheManagers = new ArrayList<CacheManager>();private boolean fallbackToNoOpCache = false;/*** Construct an empty CompositeCacheManager, with delegate CacheManagers to* be added via the {@link #setCacheManagers "cacheManagers"} property.*/public CompositeCacheManager() {}/*** Construct a CompositeCacheManager from the given delegate CacheManagers.* @param cacheManagers the CacheManagers to delegate to*/public CompositeCacheManager(CacheManager... cacheManagers) {setCacheManagers(Arrays.asList(cacheManagers));}/*** Specify the CacheManagers to delegate to.*/public void setCacheManagers(Collection<CacheManager> cacheManagers) {this.cacheManagers.addAll(cacheManagers);}/*** Indicate whether a {@link NoOpCacheManager} should be added at the end of the delegate list.* In this case, any {@code getCache} requests not handled by the configured CacheManagers will* be automatically handled by the {@link NoOpCacheManager} (and hence never return {@code null}).*/public void setFallbackToNoOpCache(boolean fallbackToNoOpCache) {this.fallbackToNoOpCache = fallbackToNoOpCache;}@Overridepublic void afterPropertiesSet() {if (this.fallbackToNoOpCache) {this.cacheManagers.add(new NoOpCacheManager());}}@Overridepublic Cache getCache(String name) {for (CacheManager cacheManager : this.cacheManagers) {Cache cache = cacheManager.getCache(name);if (cache != null) {return cache;}}return null;}@Overridepublic Collection<String> getCacheNames() {Set<String> names = new LinkedHashSet<String>();for (CacheManager manager : this.cacheManagers) {names.addAll(manager.getCacheNames());}return Collections.unmodifiableSet(names);}}

转载于:https://www.cnblogs.com/hujunzheng/p/10084452.html

RedisCacheManager设置Value序列化器技巧相关推荐

  1. 关于django—模型序列化器类详细内容

    一.定义模型序列化器类 1.继承serializers.ModelSerializer类或其子类 2.需要在Meta内部类中指定model.fields类属性参数 3.model指定模型类(需要生成序 ...

  2. 【Win10技巧】如何设置win10资源管理器打开为“这台电脑”?

    习惯了点击"资源管理器"后就能快速的打开自己的文件,现在win10系统上却是默认打开"快速访问"界面,虽然这种设计会让某些操作更加便捷,但是对于我们这种使用wi ...

  3. 客户端序列码生成_Django REST Framework教程(2): 序列化器介绍及开发基于函数视图的API...

    在上篇文章中,我们已经介绍了为什么要学习DRF,什么是序列化以及什么是符合RESTful规范的Web API.在本篇文章中我们将以博客为例,使用DRF提供的序列化器(Serializers类)开发两个 ...

  4. Redis序列化、RedisTemplate序列化方式大解读,介绍Genericjackson2jsonredisserializer序列化器的坑

    前言 上一篇已经介绍了优雅的操作Redis: [小家Spring]Spring Boot中使用RedisTemplate优雅的操作Redis,并且解决RedisTemplate泛型注入的问题.本篇着重 ...

  5. DRF框架—序列化器中的字段校验规则

    一.怎么校验创建的项目名是否是唯一的,当项目名name字段不唯一,怎么设置提示信息? 方法:导入UniqueValidator from rest_framework.validators impor ...

  6. JDK11下J2Cache序列化器反射异常及--illegal-access解决方案

    问题现象 最近线上部署应用时,发现如下异常: Failed to instantiate [net.oschina.j2cache.CacheChannel]: Factory method 'cac ...

  7. Django REST Framework教程(4): 玩转序列化器(Serializer)

    在前面的文章中我们以博客为例,自定义了一个简单的 ArticleSerializer 类, 并分别以函数视图(FBV)和基于类的视图(CBV)编写了博客文章列表资源和单篇文章资源的API,支持客户端以 ...

  8. 序列化器serializers的使用

    serializer.py中设置序列化器 from rest_framework import serializers from myapp.models import Poemclass PoemS ...

  9. drf快速入门01---REST规范介绍序列化器的基础使用

    一. 基本规范 REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征性状态转移). 它首次出现在2000年Roy Fielding的博士论 ...

最新文章

  1. 【转摘】Word中查找与替换的妙用
  2. EMNLP 2019中和BERT相关的一些论文介绍
  3. android 基础应用程序,android应用程序基本实现(基础篇).ppt
  4. Redis-6.2.5 安装 Linux环境(单机)
  5. Android应用模块之间通信模式
  6. json转对象的时候字段为空值的时候会被过滤
  7. 华硕笔记本显示服务器不可用,华硕笔记本电脑开不了热点咋办
  8. 设置Parallels Desktop中的Windows虚拟机使用Mac宿主机代理
  9. css背景颜色跟随文字颜色、设置文字颜色反色
  10. SpringBoot MySQL #1 报错 Error executing DDL ...
  11. 百度竞价每天如何优化
  12. 学习篇-Activiti-29-流程定义存储表
  13. 生命主题dreamweaver作业静态HTML网页设计——卫生与健康 6页 带视频
  14. 周鸿祎《智能主义》读书笔记
  15. HTTP代理IP的三种使用方法
  16. QT开发的视频监管平台分享
  17. c语言单目运算符和三目,6、单目运算符 双目运算符 三目运算符() 及 优先级的探讨!...
  18. 机器学习中常见的评估方法
  19. linux 防火墙 封端口,linux防火墙端口开放与关闭(防火墙关了端口都不通)
  20. 临江仙·夜饮东坡醒复醉

热门文章

  1. 12123两小时没付款怎么办_机械厂上班的男朋友,一天十小时,周末不休,没时间陪我怎么办?...
  2. linux根文件系统 /etc/resolv.conf 文件详解
  3. IDEA JDK1.8 ProGuard 混淆Maven项目代码
  4. 3行代码实现ftp 上传、下载、删除操作集合
  5. Jenkins_GithubFork程序_入门试炼04
  6. 编写一个C程序,实现以下功能:编写一个函数decTobin(int n),该函数能将一个十进制数n转换成二进制数,输入13 输出 1101。在main函数中输入整数n,调用函数,输出它的二进制
  7. java 反射类 实例化_java-如何在Android上通过反射实例化成员类
  8. opencv论坛_Opencv批量添加logo的解决方案
  9. android中设置lmargin简书,超详细React Native实现微信好友/朋友圈分享功能-Android/iOS双平台通用...
  10. java内嵌excel_如何在Excel中嵌入URL中的图像?