由于@Cacheable在使用过程中没有无法支持时间配置,无法根据业务需要设置相应redis缓存失效时间。本文提供一个方法可以支持redis缓存时间配置。
废话不多说,直接上代码
1 新增一个注解CacheAbleDuration,用于支持时间得配置
package com.example.demo.redis.annotation; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.Cacheable; import org.springframework.core.annotation.AliasFor; import java.lang.annotation.*; import java.util.concurrent.Callable; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface CacheAbleDuration { long ttl() default 0; DurationTypeEnum durationType() default DurationTypeEnum.SECONDS; }
2 DurationTypeEnum 时间类型枚举类型 package com.example.demo.redis.annotation; public enum DurationTypeEnum { MILLISECONDS,SECONDS,MINUTES,HOURS,DAYS; }
3扩展RedisCache类型
package com.example.demo.redis.annotation; import org.springframework.data.redis.cache.RedisCache; import org.springframework.lang.Nullable; import java.time.Duration; public class ExtendRedisCache extends RedisCache { final private long ttl; final private DurationTypeEnum durationTypeEnum; public long getTtl() { return ttl; } public DurationTypeEnum getDurationTypeEnum() { return durationTypeEnum; } protected ExtendRedisCache(RedisCache redisCache, long ttl, DurationTypeEnum durationTypeEnum) { super(redisCache.getName(), redisCache.getNativeCache(), redisCache.getCacheConfiguration()); this.ttl = ttl; this.durationTypeEnum = durationTypeEnum; } @Override public void put(Object key, @Nullable Object value) { Object cacheValue = preProcessCacheValue(value); if (!isAllowNullValues() && cacheValue == null) { throw new IllegalArgumentException(String.format( "Cache '%s' does not allow 'null' values. Avoid storing null via '@Cacheable(unless=\"#result == null\")' or configure RedisCache to allow 'null' via RedisCacheConfiguration.", getName())); } if (ttl > 0) { getNativeCache().put(getName(), createAndConvertCacheKey(key), serializeCacheValue(cacheValue), getDuration()); } else { getNativeCache().put(getName(), createAndConvertCacheKey(key), serializeCacheValue(cacheValue), getCacheConfiguration().getTtl()); } } private byte[] createAndConvertCacheKey(Object key) { return serializeCacheKey(createCacheKey(key)); } private Duration getDuration() { if (DurationTypeEnum.SECONDS == this.durationTypeEnum) { return Duration.ofSeconds(ttl); } if (DurationTypeEnum.MINUTES == this.durationTypeEnum) { Duration.ofMinutes(ttl); } if (DurationTypeEnum.DAYS == this.durationTypeEnum) { Duration.ofDays(ttl); } if (DurationTypeEnum.MILLISECONDS == this.durationTypeEnum) { Duration.ofMillis(ttl); } return Duration.ofSeconds(ttl); } }
4重写SimpleCacheResolver方法:resolveCaches
package com.example.demo.redis.annotation; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.interceptor.*; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.data.redis.cache.RedisCache; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class ExtendSimpleCacheResolver extends SimpleCacheResolver { private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16); private final Class<CacheAbleDuration> CACHE_OPERATION_CLASS = CacheAbleDuration.class; public ExtendSimpleCacheResolver() { } public ExtendSimpleCacheResolver(CacheManager cacheManager) { super(cacheManager); } @Override public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) { Collection<? extends Cache> caches = super.resolveCaches(context); BasicOperation operation = context.getOperation(); if (!(operation instanceof CacheableOperation)) { return caches; } //判断是否有CacheAbleDuration注解 Method method = context.getMethod(); CacheDurationConfig cacheDurationConfig = parseCacheAnnotationsCacheDurationConfig(method); if (cacheDurationConfig == null) { return caches; } Collection<Cache> objects = new ArrayList<>(caches.size()); for (Cache cache : caches) { if (cache instanceof RedisCache) { Cache cache1 = cacheMap.get(cache.getName()); if (cache1 == null) { cache1 = new ExtendRedisCache((RedisCache) cache, cacheDurationConfig.getTtl(), cacheDurationConfig.getDurationTypeEnum()); cacheMap.put(cache.getName(), cache1); } objects.add(cache1); } else { objects.add(cache); } } return objects; } private CacheDurationConfig parseCacheAnnotationsCacheDurationConfig( AnnotatedElement ae) { Collection<? extends Annotation> anns = AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_CLASS); if (anns != null) { return getCacheDurationConfig((CacheAbleDuration) anns.iterator().next()); } anns = AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_CLASS); if (anns.isEmpty()) { return null; } return getCacheDurationConfig((CacheAbleDuration) anns.iterator().next()); } private CacheDurationConfig getCacheDurationConfig(CacheAbleDuration cacheAbleDuration) { return new CacheDurationConfig(cacheAbleDuration.ttl(), cacheAbleDuration.durationType()); } }
private CacheDurationConfig parseCacheAnnotationsCacheDurationConfig( AnnotatedElement ae) { Collection<? extends Annotation> anns = AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_CLASS); if (anns != null) { return getCacheDurationConfig((CacheAbleDuration) anns.iterator().next()); } anns = AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_CLASS); if (anns.isEmpty()) { return null; } return getCacheDurationConfig((CacheAbleDuration) anns.iterator().next()); } private CacheDurationConfig getCacheDurationConfig(CacheAbleDuration cacheAbleDuration) { return new CacheDurationConfig(cacheAbleDuration.ttl(), cacheAbleDuration.durationType()); } }
package com.example.demo.redis.annotation; import java.io.Serializable; public class CacheDurationConfig implements Serializable { private long ttl; private DurationTypeEnum durationTypeEnum; public CacheDurationConfig(long ttl, DurationTypeEnum durationTypeEnum) { this.ttl = ttl; this.durationTypeEnum = durationTypeEnum; } public long getTtl() { return ttl; } public void setTtl(long ttl) { this.ttl = ttl; } public DurationTypeEnum getDurationTypeEnum() { return durationTypeEnum; } public void setDurationTypeEnum(DurationTypeEnum durationTypeEnum) { this.durationTypeEnum = durationTypeEnum; } }
6 配置 ExtendSimpleCacheResolver
package com.example.demo.controller; import com.example.demo.redis.annotation.ExtendSimpleCacheResolver; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.*; import org.springframework.data.redis.cache.RedisCacheManager; @Configuration public class RedisConfig { @Bean("extendSimpleCacheResolver") public ExtendSimpleCacheResolver getExtendSimpleCacheResolver(@Autowired RedisCacheManager redisCacheManager){ return new ExtendSimpleCacheResolver(redisCacheManager); } }
应用例子:
package com.example.demo.controller; import com.example.demo.redis.annotation.CacheAbleDuration; import com.example.demo.redis.annotation.DurationTypeEnum; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @Service public class TestServiceImpl { @CacheAbleDuration(ttl = 10000,durationType = DurationTypeEnum.SECONDS) @Cacheable(value="ttttttttttttt",key = "1111111",cacheResolver = "extendSimpleCacheResolver") public String testCacheAbleDural(){ return "ttttttttttttttttttttttttttttt"; } }
结果:
如果有错误,请指正。