主要实现代码如下:
/**
* 1. 支持解析 @CacheConfig.cacheNames ,独立为每个方法设置过期时间,单位秒
* 2. 返回增强后的Cache实例 :new RedisPlusCache
*
* @see RedisPlusCache
**/
public class RedisCachePlusManager extends RedisCacheManager {
private final CachePlusConfig config;
public RedisCachePlusManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, CachePlusConfig config) {
super(cacheWriter, defaultCacheConfiguration);
this.config = config.clone();
}
@NonNull
@Override
protected RedisCache createRedisCache(@NonNull String name, @Nullable RedisCacheConfiguration cacheConfig) {
String[] array = name.split(this.config.getDelimiter());
name = array[0];
if (array.length > 1 && cacheConfig != null) {
long ttl = Long.parseLong(array[1]);
cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(ttl));
}
if (array.length > 2) {
this.config.setDuration(Long.parseLong(array[2]));
}
RedisCache redisCache = super.createRedisCache(name, cacheConfig);
return new RedisPlusCache(redisCache.getName(), redisCache.getNativeCache(), redisCache.getCacheConfiguration(), this.config);
}
}
/**
* 1. 基于CaffeineCache实现二级缓存
*
* @see CachePlusConfig
**/
public class RedisPlusCache extends RedisCache {
private final Map<Long, Cache<Object, Object>> caffeineCaches;
private final CachePlusConfig config;
protected RedisPlusCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfig, CachePlusConfig config) {
super(name, cacheWriter, cacheConfig);
this.caffeineCaches = new ConcurrentHashMap<>();
this.config = config;
}
@Override
protected Object lookup(@NonNull Object key) {
RedisCacheConfiguration cacheConfiguration = super.getCacheConfiguration();
Duration duration = cacheConfiguration.getTtl();
Cache<Object, Object> localCache = this.caffeineCaches.get(duration.getSeconds());
if (localCache == null) return this.lookupAndPutLocal(key);
Object v = localCache.get(key, oneKey -> null);
return v == null ? this.lookupAndPutLocal(key) : v;
}
@Override
public void put(@NonNull Object key, @Nullable Object value) {
this.putLocal(key, value);
super.put(key, value);
}
private Object lookupAndPutLocal(@NonNull Object key) {
Object v = super.lookup(key);
this.putLocal(key, v);
return v;
}
private void putLocal(Object key, Object value) {
Object cacheValue = preProcessCacheValue(value);
if (cacheValue != null) {
Cache<Object, Object> localCache = this.createLocalCache();
localCache.put(key, cacheValue);
}
}
private Cache<Object, Object> createLocalCache() {
long time = this.getCacheConfiguration().getTtl().getSeconds();
Cache<Object, Object> localCache = this.caffeineCaches.get(time);
if (localCache != null) return localCache;
synchronized (this) {
localCache = this.caffeineCaches.get(time);
if (localCache != null) return localCache;
localCache = Caffeine.newBuilder()
.initialCapacity((int) (this.config.getDuration() >> 2))
.expireAfterWrite(this.config.getDuration(), TimeUnit.SECONDS)
.maximumSize(this.config.getMaximumSize()).build();
this.caffeineCaches.put(time, localCache);
return localCache;
}
}
}
/**
* 缓存配置类
**/
@Data
public class CachePlusConfig implements Serializable, Cloneable {
// 分隔符
private String delimiter;
// 过期时间
private long duration;
// 最大缓存容量
private long maximumSize;
public CachePlusConfig() {
this.delimiter = "##";
this.duration = 600L;
this.maximumSize = 100_000L;
}
@Override
public CachePlusConfig clone() {
try {
return (CachePlusConfig) super.clone();
} catch (CloneNotSupportedException e) {
throw new IllegalStateException("clone error", e);
}
}
}
注册类:
@Configuration
@EnableCaching
public class SpringCacheConfig {
@Resource
@Qualifier("redisConnectionFactory")
private RedisConnectionFactory redisConnectionFactory;
@Resource
private CachePlusConfig localCachePlusConfig;
@ConfigurationProperties("cache-plus.local")
@Bean
public CachePlusConfig get() {
return new CachePlusConfig();
}
@Bean("commonKeyGenerator")
public KeyGenerator getCommonKeyGenerator() {
return new CommonKeyGenerator();
}
@Primary
@Bean
public CacheManager cacheManager() {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(300));
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
redisCacheConfiguration = redisCacheConfiguration.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(genericFastJsonRedisSerializer));
return new RedisCachePlusManager(RedisCacheWriter.lockingRedisCacheWriter(redisConnectionFactory), redisCacheConfiguration, localCachePlusConfig);
}
@Bean("localCacheManager")
public CacheManager localCacheManager() {
CaffeineCacheManager localCacheManager = new CaffeineCachePlusManager(this.localCachePlusConfig);
localCacheManager.setAllowNullValues(true);
return localCacheManager;
}
}
@Configuration
public class RedisCommonConfig {
@ConditionalOnMissingBean(name = "redisTemplate")
@Bean(name = "redisTemplate")
public RedisTemplate<String, Object> redisTemplate(@Qualifier("redisConnectionFactory") RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// 设置value的序列化规则和 key的序列化规则
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(genericFastJsonRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setHashValueSerializer(genericFastJsonRedisSerializer);
redisTemplate.setDefaultSerializer(genericFastJsonRedisSerializer);
redisTemplate.setEnableDefaultSerializer(true);
return redisTemplate;
}
@ConditionalOnMissingBean(name = "redisConnectionFactory")
@Bean(name = "redisConnectionFactory")
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration("127.0.0.1", 6379);
return new LettuceConnectionFactory(configuration);
}
}
扩展,单独注册只支持二级缓存(本地缓存)的CacheManager,基于CaffeineCache实现:
/**
* 1. 支持解析 @CacheConfig.cacheNames ,独立为每个方法设置过期时间,单位秒
**/
@Slf4j
public class CaffeineCachePlusManager extends CaffeineCacheManager {
private final CachePlusConfig config;
public CaffeineCachePlusManager(CachePlusConfig config) {
this.config = config;
}
@NonNull
@Override
protected com.github.benmanes.caffeine.cache.Cache<Object, Object> createNativeCaffeineCache(@NonNull String name) {
long expire = this.getExpire(name);
if (expire > 0) {
return Caffeine.newBuilder()
.initialCapacity((int)(this.config.getMaximumSize() >> 4))
// 3600秒后自动删除
.expireAfterWrite(expire, TimeUnit.SECONDS)
// 最大容量10w个,超过会自动清理空间
.maximumSize(this.config.getMaximumSize())
.removalListener(((key, value, cause) -> {
//清理通知 key, value ==> 键值对 cause ==> 清理原因
log.info("caffeine cache removed: {}, {}, {}", key, value, cause);
})).build();
}
return super.createNativeCaffeineCache(name);
}
private long getExpire(@NonNull String name) {
if (this.config.getDelimiter() == null) return this.config.getDuration();
String[] split = name.split(this.config.getDelimiter());
return split.length > 1 ? Long.parseLong(split[1]) : this.config.getDuration();
}
}
扩展,键生成器
/**
* 通用的缓存键生成器
**/
public class CommonKeyGenerator implements KeyGenerator {
@NonNull
@Override
public Object generate(Object target, Method method, @NonNull Object... params) {
return target.getClass().getName() + ":" + method.getName() + ":" + generateKey(params);
}
/**
* Generate a key based on the specified parameters.
*/
public static Object generateKey(Object... params) {
if (params.length == 0) {
return SimpleKey.EMPTY;
}
if (params.length == 1) {
Object param = params[0];
if (param != null && !param.getClass().isArray()) {
return param;
}
}
return StringUtils.arrayToDelimitedString(params, "-");
}
}
/**
* 缓存键生成器-从方法开始拼接
**/
public class MethodKeyGenerator implements KeyGenerator {
@NonNull
@Override
public Object generate(@NonNull Object target, Method method, @NonNull Object... params) {
return method.getName() + ":" + CommonKeyGenerator.generateKey(params);
}
}
业务代码示例:
@CacheConfig(cacheNames = "DICT##120", keyGenerator = "commonKeyGenerator")
@Service
public class RegionService {
// 过期时间、键生成,继承@CacheConfig
@Cacheable
public String getCityName(String code) {
if ("010".equals(code)) return "beijing";
else if (("020".equals(code))) return "shanghai";
else return "shenzhen";
}
@Cacheable
public String getCityName(String code, String lang) {
if ("010".equals(code)) return "beijing";
else if (("020".equals(code))) return "shanghai";
else return "shenzhen";
}
// 该方法独立设置过期时间240秒
@Cacheable(cacheNames = "DICT##240")
public String getCityName240(String code, String lang) {
if ("010".equals(code)) return "beijing";
else if (("020".equals(code))) return "shanghai";
else return "shenzhen";
}
// 该方法只支持二级缓存
@Cacheable(cacheNames = "DICT##10", cacheManager = "localCacheManager")
public String getCityNameByLocal(String code) {
if ("010".equals(code)) return "beijing";
else if (("020".equals(code))) return "shanghai";
else return "shenzhen";
}
}