赞
踩
主要实现代码如下:
- /**
- * 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";
- }
-
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。