Spring 一个服务 bean 中的多个缓存

Spring multi cache in one Service bean

我正在使用 spring-4.3.

@Cacheable("productCategories")
@Override
public ProductCategory get(Object id) throws BeanNotFoundException {
    return super.get(id);
}

@Cacheable("productCategoryChildren")
public List<ProductCategory> getChildren(String parentId) {
    return productCategoryDao.getChildren(parentId);
}

首先我调用 get("100") 和 return 一个实体,然后调用 getChildren("100"),我得到一个错误 java.lang.ClassCastException: com.jxs.ms.entity.ProductCategory cannot be cast to java.util.List。这两种方法是否使用相同的缓存来存储值?

我在 Joshua White's Blog 中发现了问题。

Warning: When dealing with other caching solutions, Spring’s CacheManger usually contains a map of Cache (each implementing map like functionality) implementations that are backed by separate caches. Using the default RedisCacheManager configuration, this is not the case. Based on the javadoc comment on the RedisCacheManager, its not clear if this is a bug or simply incomplete documentation. “…By default saves the keys by appending a prefix (which acts as a namespace).”

While the DefaultRedisCachePrefix which is configured in the RedisCacheManager certainly supports this, it is not enabled by default. As a result, when you ask the RedisCacheManager for a Cache of a given name, it simply creates a new Cache instance that points to the same database. As a result, the Cache instances are all the same. The same key will retrieve the same value in all Cache instances.

As the javadoc comment alludes to, prefixs can be used to setup client managed (Redis doesn’t support this functionality natively) namespaces that essentially create “virtual” caches within the same database. You can turn this feature on by calling redisCacheManager.setUsePrefix(true) either using the Spring XML or Java configuration.

@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
    RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
    cacheManager.setDefaultExpiration(3600 * 2);
    cacheManager.setUsePrefix(true);//this solved my problem
    return cacheManager;
}

我遇到了类似的问题,但就我而言,我们使用的是 spring 和 memcached。我们的问题是 CacheConfiguration 将 useNameAsKeyPrefix 属性 默认为 false。

要理解的关键是两个命名缓存的底层缓存使用相同的映射。在这种情况下,idparentId 可能最终成为相同的缓存键。尽管缓存在 spring 缓存抽象中的名称不同,但底层缓存管理器将它们全部存储在同一个位置。因此,对 get("1234") 的第一个请求使用给定 ID“1234”的 ProductCategory 填充缓存。然后当 getChildren("1234") 被调用时,缓存管理器执行忽略缓存名称的查找,只查找它找到的键“1234”和 returns a ProductCategory。因为 spring 代理 bean 需要一个 List<ProductCategory>,你最终得到一个 ClassCastException

像这样更新配置,解决了我的问题,:

<bean id="defaultMemCacheConfiguration" class="com.google.code.ssm.providers.CacheConfiguration">
    ...
    <property name="useNameAsKeyPrefix" value="true"/>
</bean>