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

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

springboot2.x 与redis整合的初级篇已完结,本篇会讨论一些稍微高级的玩法。继上一篇,我们已经

可以在正常开发中使用缓存,但是可能开发中可能还会有一些特殊的要求。例如:我想在method上自定义缓存

有效期,而不是所有的缓存用同一个有效期.

### 通过方法注解,标记缓存过期时间

首先定义一个方法注解CacheExpire,value可以指定有效时间,默认是24小时

```

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.METHOD, ElementType.TYPE})

public @interface CacheExpire {

//Sets the expire time (in seconds).

long value() default 24*60*60;

}

```

### 注解解析

通过[springboot2.x redis 整合-初级](http://www.threeperson.com/users/1/articles/2143) 我们知道,缓存的有效期是CacheManger 初始化时,通过RedisCacheConfiguration entryTtl(Duration ttl) 初始化的,那么现在就有一个问题。CacheManager 初始化前@CacheExpire标记的类必须提前加载,否则我们无法提取

对应的缓存信息。事实上RedisCacheManager 已经想到了这一点,并且支持最后初始化的机制。我们可以看下RedisCacheManager 继承关系图;

![RedisCacheManager](http://www.bbvdd.com/d/20181205155418z6a.png)

你会发现InitializingBean 这个接口,该接口afterPropertiesSet 会在所有的类属性信息加载完毕后执行。

有同学会问了,RedisCacheManager在构造函数中初始化的配置,name在类属性装在完毕后执行afterPropertiesSet依然无济于事。那我们看看父类中是否提供了其他入口。AbstractCacheManager 实现了InitializingBean接口的afterPropertiesSet 方法。并且暴露一个loadCaches抽象方法。

该方法就是用来加载各个缓存空间对应的缓存的。

```

public abstract class AbstractCacheManager implements CacheManager, InitializingBean {

private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);

private volatile Set<String> cacheNames = Collections.emptySet();

// Early cache initialization on startup

@Override

public void afterPropertiesSet() {

initializeCaches();

}

/**

* Initialize the static configuration of caches.

* <p>Triggered on startup through {@link #afterPropertiesSet()};

* can also be called to re-initialize at runtime.

* @since 4.2.2

* @see #loadCaches()

*/

public void initializeCaches() {

Collection<? extends Cache> caches = loadCaches();

synchronized (this.cacheMap) {

this.cacheNames = Collections.emptySet();

this.cacheMap.clear();

Set<String> cacheNames = new LinkedHashSet<>(caches.size());

for (Cache cache : caches) {

String name = cache.getName();

this.cacheMap.put(name, decorateCache(cache));

cacheNames.add(name);

}

this.cacheNames = Collections.unmodifiableSet(cacheNames);

}

}

/**

* Load the initial caches for this cache manager.

* <p>Called by {@link #afterPropertiesSet()} on startup.

* The returned collection may be empty but must not be {@code null}.

*/

protected abstract Collection<? extends Cache> loadCaches();

```

### 自定义CacheManager

```

public class CustomerCacheManager extends RedisCacheManager implements ApplicationContextAware, InitializingBean {

private static final Logger logger = LoggerUtils.getLogger(CubeCacheManager.class);

private ApplicationContext applicationContext;

private RedisCacheConfiguration defaultCacheConfig;

Map<String, RedisCacheConfiguration> cacheConfigurationMap = Maps.newHashMap();

public CubeCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {

super(cacheWriter, defaultCacheConfiguration);

this.defaultCacheConfig = defaultCacheConfiguration;

}

@Override

protected Collection<RedisCache> loadCaches() {

List<RedisCache> caches = new LinkedList<>();

Map<String, Long> cacheDurationMap = parseCacheDuration();

cacheDurationMap.forEach((key, value) -> {

RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().entryTtl(defaultCacheConfig.getTtl()).serializeValuesWith(defaultCacheConfig.getValueSerializationPair());

if (null != value) {

redisCacheConfiguration.entryTtl(Duration.ofSeconds(value));

}

cacheConfigurationMap.put(key, redisCacheConfiguration);

});

for (Map.Entry<String, RedisCacheConfiguration> entry : cacheConfigurationMap.entrySet()) {

caches.add(createRedisCache(entry.getKey(), entry.getValue()));

}

return caches;

}

@Override

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

this.applicationContext = applicationContext;

}

private Map<String, Long> parseCacheDuration() {

final Map<String, Long> cacheExpires = new HashMap<>();

String[] beanNames = applicationContext.getBeanNamesForType(Object.class);

for (String beanName : beanNames) {

final Object obj = applicationContext.getBean(beanName);

Repository service = AnnotationUtils.findAnnotation(obj.getClass(), Repository.class);

if (null == service) {

continue;

}

addCacheExpires(obj, cacheExpires);

}

return cacheExpires;

}

private void addCacheExpires(final Object obj, final Map<String, Long> cacheExpires) {

Class clazz = obj.getClass();

ReflectionUtils.doWithMethods(obj.getClass(), method -> {

ReflectionUtils.makeAccessible(method);

CacheExpire cacheExpire = findCacheDuration(clazz, method);

Cacheable cacheable = AnnotationUtils.findAnnotation(method, Cacheable.class);

Set<String> cacheNames = findCacheNames(obj, cacheable);

for (String cacheName : cacheNames) {

cacheExpires.put(cacheName, cacheExpire.value());

}

}, method -> null != AnnotationUtils.findAnnotation(method, Cacheable.class));

}

/**

* CacheDuration标注的有效期,优先使用方法上标注的有效期

*

* @param clazz

* @param method

* @return

*/

private CacheExpire findCacheDuration(Class clazz, Method method) {

CacheExpire methodCacheDuration = AnnotationUtils.findAnnotation(method, CacheExpire.class);

if (null != methodCacheDuration) {

return methodCacheDuration;

}

CacheExpire classCacheDuration = AnnotationUtils.findAnnotation(clazz, CacheExpire.class);

if (null != classCacheDuration) {

return classCacheDuration;

}

return null;

}

private Set<String> findCacheNames(Object obj, Cacheable cacheable) {

Object target = null;

try {

target = AopTargetUtils.getTarget(obj);

} catch (Exception e) {

logger.error("get proxy target fail", e);

}

if (null != target) {

return ArrayUtils.isEmpty(cacheable.value()) ? new HashSet(Arrays.asList(target.getClass().getName())) : new HashSet(Arrays.asList(cacheable.value()));

}

return new HashSet<>();

}

}

```

### 初始化CustomerCacheManager bean

```

@Configuration

@EnableCaching

public class RedisConfig extends CacheConfig {

private CacheManager customerCacheManager;

@Autowired

public void setCustomerCacheManager(RedisConnectionFactory factory) {

RedisCacheConfiguration defaultCacheConfig = getRedisCacheConfiguration();

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

}

}

```

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


评论