threeperson
发布于 2018-12-05 / 0 阅读
0
0

springboot2.x redis 整合-自定义缓存空间

经过前连篇整合介绍,基本可以满足大多数使用场景了,作为矫情的码农来讲,除了灵活可控的缓存时间,还想简化

缓存名称设置,干脆完全不设置,在通用层统一处理缓存空间和key的生成。

### 期望使用方式如下

@Cacheable 无需设置value 和key

```

@Cacheable

@CacheExpire(value = 2 * DateUtils.ONE_DAY_SECONDS)

public ThirdUser query(@KeyParam String openId) {

return db.from(ThirdUser.class).where("open_id", openId).first(ThirdUser.class);

}

```

### 如果Cacheable 不设置value 会怎样呢

```

java.lang.IllegalStateException: No cache could be resolved for 'Builder[public cn.cube.gopher.entity.ThirdUser cn.cube.gopher.dao.impl.ThirdUserDao.query(java.lang.String)] caches=[] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'' using resolver 'org.springframework.cache.interceptor.SimpleCacheResolver@63cf578f'. At least one cache should be provided per cache operation.

at org.springframework.cache.interceptor.CacheAspectSupport.getCaches(CacheAspectSupport.java:255)

at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContext.<init>(CacheAspectSupport.java:707)

at org.springframework.cache.interceptor.CacheAspectSupport.getOperationContext(CacheAspectSupport.java:265)

at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContexts.<init>(CacheAspectSupport.java:598)

```

### 添加CacheResolver

通过CacheAspectSupport 我们知道,如果不设置Cacheable value属性,就会报错。那么我们看一下他的实现逻辑。

```

protected Collection<? extends Cache> getCaches(

CacheOperationInvocationContext<CacheOperation> context, CacheResolver cacheResolver) {

Collection<? extends Cache> caches = cacheResolver.resolveCaches(context);

//cacheResolver 获取caches为空,所以抛出IllegalStateException异常

if (caches.isEmpty()) {

throw new IllegalStateException("No cache could be resolved for '" +

context.getOperation() + "' using resolver '" + cacheResolver +

"'. At least one cache should be provided per cache operation.");

}

return caches;

}

```

现在我们知道是因为CacheResolver.resolveCaches为空导致抛出异常,事实上我们根本就没设置缓存空间,当然就不会存在对一个的cache。这也正是我们要处理的,处理办法就是用当前类全路径作为该cache的name。现在我们还有一个问题,就是CacheResolver是从哪里设置的呢。就在CachingConfigurerSupport 中。CachingConfigurerSupport 为缓存暴露了一些自定义接口,其中就有一个cacheResolver函数返回值CacheResolver,下面我们来自定义个CacheResolver。

```

public class CustomerCacheResolver extends SimpleCacheResolver {

public CubeCacheResolver(){

super();

}

public CubeCacheResolver(CacheManager cacheManager) {

super(cacheManager);

}

@Override

protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {

Set<String> cacheNames = context.getOperation().getCacheNames();

#当发现没有设置cacheName时,把目标类的全路径名设置为缓存空间名

if (CollectionUtils.isEmpty(cacheNames)){

String cacheName = context.getTarget().getClass().getName();

cacheNames.add(cacheName);

}

return cacheNames;

}

}

```

重写CachingConfigurerSupport cacheResolver函数

```

@Configuration

@EnableCaching

public class RedisConfig extends CacheConfig {

private CacheManager cacheManager;

@Autowired

public void setCacheManager(RedisConnectionFactory factory) {

RedisCacheConfiguration defaultCacheConfig = getRedisCacheConfiguration();

cacheManager = new CustomerCacheManager(RedisCacheWriter.nonLockingRedisCacheWriter(factory),defaultCacheConfig);

}

@Override

public CacheResolver cacheResolver() {

return new CustomerCacheResolver(cacheManager);

}

}

```

转载保留出处[http://www.threeperson.com/users/zld406504302/articles/2145)


评论