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;
} @Override
public 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;
} @Override
public 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;
} @Override
public 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;
} @Override
public 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()))
);
} @Override
public 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 { @Override
protected 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;
} @Override
public void afterPropertiesSet() {
if (this.fallbackToNoOpCache) {
this.cacheManagers.add(new NoOpCacheManager());
}
} @Override
public Cache getCache(String name) {
for (CacheManager cacheManager : this.cacheManagers) {
Cache cache = cacheManager.getCache(name);
if (cache != null) {
return cache;
}
}
return null;
} @Override
public Collection<String> getCacheNames() {
Set<String> names = new LinkedHashSet<String>();
for (CacheManager manager : this.cacheManagers) {
names.addAll(manager.getCacheNames());
}
return Collections.unmodifiableSet(names);
} }

RedisCacheManager设置Value序列化器技巧的更多相关文章

  1. Django:RestFramework之-------序列化器

    8.序列化 功能: 对请求数据进行验证 对Queryset进行序列化 8.1一个简单序列化: import json from api import models from rest_framewor ...

  2. 开源!一款功能强大的高性能二进制序列化器Bssom.Net

    好久没更新博客了,我开源了一款高性能的二进制序列化器Bssom.Net和新颖的二进制协议Bssom,欢迎大家Star,欢迎参与项目贡献! Net开源技术交流群 976304396,禁止水,只能讨论技术 ...

  3. Android 使用xml序列化器生成xml文件

    在<Android 生成xml文件>一文中使用流的形式写入xml格式文件,但是存在一定的问题,那就是在短信内容中不能出现<>之类的括号,本文使用xml序列化器来解决 xml序列 ...

  4. 使用XMl序列化器生成xml文件

    生成XML文件 创建几个虚拟的短信对象,存在list中 备份数据通常都是备份至sd卡 使用StringBuffer拼接字符串 把整个xml文件所有节点append到sb对象里 sb.append(&q ...

  5. ASP.NET MVC4 json序列化器

    ASP.NET MVC4中调用WEB API的四个方法 2012年06月07日00:05 it168网站原创 作者:廖煜嵘 编辑:景保玉 我要评论(0) [IT168技术]当今的软件开发中,设计软件的 ...

  6. WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer)

    原文:WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济 ...

  7. 使用紧凑的序列化器,数倍提升性能 —— ESFramework 4.0 快速上手(11)

    在分布式通信系统中,网络传递的是二进制流,而内存中是我们基于对象模型构建的各种各样的对象,当我们需要将一个对象通过网络传递给另一个节点时,首先需要将其序列化为字节流,然后通过网络发送给目标节点,目标节 ...

  8. xml生成方式二(Xml序列化器XmlSerializer)

    一.andoirdAPI提供了xml生成和解析的API: XmlSerializer xs = Xml.newSerializer();和XmlPullParser xmlPullParser = X ...

  9. DRF 序列化器-Serializer (2)

    作用 1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串 2. 完成数据校验功能 3. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器 ...

随机推荐

  1. Cookie禁用了,Session还能用吗?原因详解

    Cookie与 Session,一般认为是两个独立的东西,Session采用的是在服务器端保持状态的方案,而Cookie采用的是在客户端保持状态的方案.但为什么禁用Cookie就不能得到Session ...

  2. Data Preprocess

    本文试图解决一个问题,即我们自定义的数据如何训练模型?初识深度学习,我们接触手写数字识别模型,但是批次数据是mnist已经定义好的,我们现在有自己的图片如何做成批次进行训练模型. 现在我们将准备好的原 ...

  3. Spring JDBC概述

    1.jdbc 概述 Spring JDBC是Spring框架的持久层子框架.用于对数据库的操作(增删改查). 而JdbcTemplate它是spring jdbc子框架中提供的一个操作类,用于对原始J ...

  4. bzoj1123 割点性质应用

    删掉无向图上任意一点,请求出将会增加的不连通的点对数 将无向图联通性的问题转化到搜索树方向上考虑 如果一个点不是割点,那么删掉该点的答案很简单,就是2*(n-1) 如果点u是割点,同时u在搜索树上有t ...

  5. cf161d 求距离为k的点对(点分治,树形dp)

    点分治裸题,但是用树形dp也能做 /* dp[u][k]表示在u下距离k的点数量 */ #include<bits/stdc++.h> using namespace std; ]; ], ...

  6. 复习reactnative....

    import React, { Component } from 'react'; import { AppRegistry, Text, Image, View, TextInput, Scroll ...

  7. IDEA 创建Spring MVC项目搭建

    概述 IntelliJ IDEA是一款更加集成智能的开发工具,相对Myeclipse开发而言,使用起来相对更加的方便:初步手动使用IDEA搭建Spring MVC项目,现将操作流程整理记录如下. 环境 ...

  8. linux改权限

    改变文件夹本身权限,不改动子文件(夹) chmod 600 my/ 改变文件夹及子目录下所有文件(夹)权限 chmod -R 777 my/ 统一修改 cd my 修改文件夹权限为755 find - ...

  9. ynoi2018

    题解: 全分块是啥操作啊.. 而且都好难.. 1.未来日记 这个比较简单 对每个块开个线段树维护权值 $n\sqrt{n}logn$ 这个会炸空间 并不能做... 但还是说一下做法 首先考虑分块 然后 ...

  10. RN错误随笔 - Unable to resolve module 'AccessibilityInfo'

    错误信息:.React Native 运行报错:Unable to resolve module 'AccessibilityInfo' 可以看到在 异常的返回的JSON 结构中给出了推荐的解决方法 ...