vCacheManager基本配置
请参考博文:springboot2.0 redis EnableCaching的配置和使用
vRedisCacheManager构造函数
/**
* 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实例)。
v设置全局通用的序列化器
v 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);
}
}v 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 +
'}';
}
}调试发现,序列化内容加入了对象的类型信息,如下。
v 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]]。
v 不同cache设置不同序列化器
v 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存储的对象类型。
v 自定义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
@Primarypublic 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;
}
}用户根据缓存名称设置不同的存储类型
@Componentpublic 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)));
}
}v 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);
}
}
共同學習,寫下你的評論
評論加載中...
作者其他優質文章






