SpringBoot2.0的CacheManager配置

spring boot 2.0的cache config变化很大, 普遍使用了build模式. 

一个比较简单的配置:

@Bean(name = "cacheManager")
@Primary
public CacheManager cacheManager(ObjectMapper objectMapper, RedisConnectionFactory redisConnectionFactory) {
    RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofSeconds(CacheTime.DEFAULT))
            .disableCachingNullValues()
            .computePrefixWith(cacheName -> "yourAppName".concat(":").concat(cacheName).concat(":"))
            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(createJackson2JsonRedisSerializer(objectMapper)));
 
    return RedisCacheManager.builder(redisConnectionFactory)
            .cacheDefaults(cacheConfiguration)
            .build();
}

 代码说明:

1. entryTtl: 定义默认的cache time-to-live.

2. disableCachingNullValues: 禁止缓存Null对象. 这个识需求而定.

3. computePrefixWith: 此处定义了cache key的前缀, 避免公司不同项目之间的key名称冲突.

4. serializeKeysWith, serializeValuesWith: 定义key和value的序列化协议, 同时的hash key和hash value也被定义.

自定义Cache key 的生成策略

在上述配置中, 生成的key是默认的SimpleKey对象, 说实在的并不好用, 容易造成redis key过长的问题. 此处, 我们自定义key的生成策略, 将方法参数转换为hashcode, 作为redis key. 需要做两个事情, 一个是添加一个自定义的ConversionService, 另一个是需要自定义一个KeyGenerator.

@Bean(name = "cacheManager")
@Primary
public CacheManager cacheManager(ObjectMapper objectMapper, RedisConnectionFactory redisConnectionFactory) {
    RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofSeconds(CacheTime.DEFAULT))
            .disableCachingNullValues()
            .computePrefixWith(cacheName -> "yourAppName".concat(":").concat(cacheName).concat(":"))
            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(createJackson2JsonRedisSerializer(objectMapper)))
            .withConversionService(new CacheKeyConversionService());
 
    return RedisCacheManager.builder(redisConnectionFactory)
            .cacheDefaults(cacheConfiguration)
            .build();
}

@Bean(name = "cacheKeyGenerator")
@Primary
 public KeyGenerator keyGenerator() {
     return (target, method, params) -> CacheHashCode.of(params);
 }

 注意到withConversionService(new CacheKeyConversionService())这句注册了一个ConversionService, 作用于Cache对象. 而KeyGenerator会作用@Cacheable注解的方法, 将方法的参数转换为hashCode. 这样的话, Cache对象和@Cacheable就打通了, 可以实现互操作. 即, 可以通过直接操纵Cache对象来读取@Cacheable方法的缓存数据. 这在实际项目中是非常有用的. 但这样做的代价是缓存容易被开发人员滥用, 做架构者不得不考虑这一点.  我也是在开发人员的强烈要求下才加入的, 后续打算通过一个配置默认关闭Cache和@Cacheable之间的互通. 

补上其他的代码:

/**
 * Convert  cache key to hash code.
 */
class CacheKeyConversionService implements ConversionService {
    @Override
    public boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType) {
        return true;
    }
 
    @Override
    public boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
        return true;
    }
 
    @Nullable
    @Override
    public <T> T convert(@Nullable Object source, Class<T> targetType) {
        return (T) convert(source);
    }
 
    @Nullable
    @Override
    public Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
        return convert(source);
    }
 
    private Object convert(Object source) {
        if (source instanceof CacheHashCode) {
            return ((CacheHashCode) source).hashString();
        }
        return CacheHashCode.of(source).hashString();
    }
}
 
/**
 * Hash code generator.
 */
static class CacheHashCode {
    private Object[] params;
    private int code;
 
    private CacheHashCode(Object[] params) {
        this.params = params;
        this.code = Arrays.deepHashCode(params);
    }
 
    public static CacheHashCode of(Object object) {
        return new CacheHashCode(ArrayUtil.isArray(object) ? ArrayUtil.toObjectArray(object) : new Object[]{object});
    }
 
    @Override
    public int hashCode() {
        return code;
    }
 
    public String hashString() {
        return code + "";
    }
 
    @Override
    public String toString() {
        return "CacheHashCode{" +
                "params=" + Arrays.toString(params) +
                ", code=" + code +
                '}';
    }
}

 

猜你喜欢

转载自rickgong.iteye.com/blog/2414263