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 继承关系图;

你会发现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)