cn.patterncat.cache.config.CascadeCacheConfig Maven / Gradle / Ivy
package cn.patterncat.cache.config;
import cn.patterncat.cache.component.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.cache.CachesEndpointAutoConfiguration;
import org.springframework.boot.actuate.cache.CachesEndpoint;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration;
import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizers;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Map;
/**
* http://localhost:8080/actuator/caches
*
* org/springframework/boot/actuate/cache/CachesEndpoint.java
* org/springframework/boot/actuate/autoconfigure/cache/CachesEndpointAutoConfiguration.java
* org/springframework/boot/actuate/cache/CachesEndpointWebExtension.java
* org/springframework/cache/interceptor/AbstractCacheInvoker.java
* org/springframework/cache/interceptor/CacheInterceptor.java
* org/springframework/cache/jcache/interceptor/CachePutInterceptor.java
* org/springframework/cache/jcache/interceptor/CacheRemoveEntryInterceptor.java
* org/springframework/cache/jcache/interceptor/CacheResultInterceptor.java
* org/springframework/boot/autoconfigure/cache/CacheConfigurations.java
*
* GenericCacheConfiguration 对应 SimpleCacheManager
* SimpleCacheConfiguration 对应 ConcurrentMapCacheManager
* RedisCacheConfiguration 对应 RedisCacheManager
* CaffeineCacheConfiguration 对应 CaffeineCacheManager
* NoOpCacheConfiguration 对应 NoOpCacheManager -- get出来的value为null,直接走原始方法
*
* Created by cat on 2019-03-12.
*/
@Configuration
@AutoConfigureAfter(CacheAutoConfiguration.class)
@AutoConfigureBefore(CachesEndpointAutoConfiguration.class)
@EnableCaching
public class CascadeCacheConfig implements CachingConfigurer {
@Autowired(required = false)
RedisCacheManager redisCacheManager;
@Autowired
CacheProperties cacheProperties;
@Autowired
CacheManagerCustomizers customizers;
CascadeCacheManager cascadeCacheManager;
CaffeineCacheManager caffeineCacheManager;
@PostConstruct
public void init(){
//NOTE 这里试图想用caffeine来做redis的fallback,但是事以愿违,因为CompositeCacheManager的getCache是根据大的cache名称来的,redis manager有该名的cache,就始终返回redis manager了,不会使用caffeine
CascadeCacheManager cacheManager = new CascadeCacheManager();
this.caffeineCacheManager = createCaffeineCacheManager();
if(redisCacheManager != null){
cacheManager.setCacheManagers(List.of(redisCacheManager,caffeineCacheManager));
}else{
cacheManager.setCacheManagers(List.of(caffeineCacheManager));
}
//NoOpCacheManager其实只存key不存value,其cache为NoOpCache
cacheManager.setFallbackToNoOpCache(false);
this.cascadeCacheManager = cacheManager;
}
/**
* 这里也不能托管,因为会导致CacheAutoConfiguration不能auto config
* 因为都有@ConditionalOnMissingBean(CacheManager.class),所以没办法自动同时都有RedisCacheManager和CaffeineCacheManager
* 这里选择让redis自动加载,手工配置caffeine,因为配置caffeine比较简单
* org/springframework/boot/autoconfigure/cache/CaffeineCacheConfiguration.java
* @return CaffeineCacheManager
*/
public CaffeineCacheManager createCaffeineCacheManager(){
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
String specification = cacheProperties.getCaffeine().getSpec();
if (StringUtils.hasText(specification)) {
cacheManager.setCacheSpecification(specification);
}
List cacheNames = cacheProperties.getCacheNames();
if (!CollectionUtils.isEmpty(cacheNames)) {
cacheManager.setCacheNames(cacheNames);
}
return customizers.customize(cacheManager);
}
/**
* NOTE 这个bean托管给spring容器的话RedisCacheConfiguration就无法初始化redisCacheManager,因而这里去掉托管,改为手工调用InitializingBean接口的afterPropertiesSet方法
* @return CacheManager
*/
@Override
public CacheManager cacheManager() {
return cascadeCacheManager;
}
@Override
public CacheResolver cacheResolver() {
return new CascadeCacheResolver(cascadeCacheManager);
}
@Override
public KeyGenerator keyGenerator() {
return null;
}
@Override
public CacheErrorHandler errorHandler() {
return new LoggingCacheErrorHandler();
}
public RedisCacheManager getRedisCacheManager() {
return redisCacheManager;
}
public CascadeCacheManager getCascadeCacheManager() {
return cascadeCacheManager;
}
public CaffeineCacheManager getCaffeineCacheManager() {
return caffeineCacheManager;
}
@Bean
public CachesEndpoint cachesEndpoint() {
//NOTE 由于cacheManager.getCache接口只返回一个,所以对于cascade的情况,这里还是分开返回cache manager ,不使用CascadeCacheManager或者CompositeCacheManager
return new CachesEndpoint(Map.of("redisCacheManager",redisCacheManager,"caffeineCacheManager",caffeineCacheManager));
}
@Bean
public CacheKeysEndpoint cacheKeysEndpoint(){
return new CacheKeysEndpoint(Map.of("redisCacheManager",redisCacheManager,"caffeineCacheManager",caffeineCacheManager));
}
@Bean
public CacheKeysEndpointWebExtension cacheKeysEndpointWebExtension(CacheKeysEndpoint cacheKeysEndpoint){
return new CacheKeysEndpointWebExtension(cacheKeysEndpoint);
}
}