springboot 整合redis注解缓存并设置时间
注意:::
当使用spring提供的注解缓存时,同一个方法是不生效的。
1 . 这种形式使用是将 cacheName后加#可以区分时间【建议使用这种,简单】
如:
// 缓存key为名称,缓存5秒 private final String template = "key#5000"; @Cacheable(cacheNames = template) public Map<String, Object> aaa() { Map<String, String> m = new HashMap<>(); m.put("a", "aaaa"); return m; }
package com.thunisoft.wfy.config; import com.thunisoft.wfy.exception.StringNotContainsException; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.cache.*; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import java.time.Duration; import java.util.Map; /** * redis 配置类 */ @Slf4j public class RedisConfigCacheManager extends RedisCacheManager { public RedisConfigCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) { super(cacheWriter, defaultCacheConfiguration); } public RedisConfigCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, String... initialCacheNames) { super(cacheWriter, defaultCacheConfiguration, initialCacheNames); } public RedisConfigCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, boolean allowInFlightCacheCreation, String... initialCacheNames) { super(cacheWriter, defaultCacheConfiguration, allowInFlightCacheCreation, initialCacheNames); } public RedisConfigCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations) { super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations); } public RedisConfigCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations, boolean allowInFlightCacheCreation) { super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations, allowInFlightCacheCreation); } private static final RedisSerializationContext.SerializationPair<Object> DEFAULT_PAIR = RedisSerializationContext.SerializationPair .fromSerializer(new GenericJackson2JsonRedisSerializer()); private static final CacheKeyPrefix DEFAULT_CACHE_KEY_PREFIX = cacheName -> cacheName+":"; @Override protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) { final int lastIndexOf = StringUtils.lastIndexOf(name, '#'); if (lastIndexOf > -1) { final String ttl = StringUtils.substring(name, lastIndexOf + 1); final Duration duration = Duration.ofSeconds(Long.parseLong(ttl)); cacheConfig = cacheConfig.entryTtl(duration); //修改缓存key和value值的序列化方式 cacheConfig = cacheConfig.computePrefixWith(DEFAULT_CACHE_KEY_PREFIX) .serializeValuesWith(DEFAULT_PAIR); final String cacheName = StringUtils.substring(name, 0, lastIndexOf); return super.createRedisCache(cacheName, cacheConfig); } throw new StringNotContainsException("字符串中必须包含#号,#号后为过期时间, -1为不过时"); } }
2. 注解形式,注意,用此方式还需要优化,如果cacheName是同一个名字,那么可能出现时间覆盖问题!!!!!
使用方式:
// 缓存 4秒 @CacheExpire(expire = 4000) @Cacheable(cacheNames = "template") public Map<String , String > aa () { return ImmutableMap.of("a", "aaaa"); }
package com.thunisoft.wfy.config; import com.thunisoft.wfy.annotation.CacheExpire; import com.thunisoft.wfy.exception.StringNotContainsException; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeansException; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Caching; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.redis.cache.*; import org.springframework.data.redis.serializer.*; import org.springframework.util.ReflectionUtils; import java.time.Duration; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * redis 配置类 * @author zhpj * @version 2.14 * @date 2020-12-08 */ @Slf4j public class RedisConfigCacheManager extends RedisCacheManager implements ApplicationContextAware { private ApplicationContext applicationContext; private final Map<String, RedisCacheConfiguration> cacheConfigurationMap = new ConcurrentHashMap<>(); public RedisConfigCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) { super(cacheWriter, defaultCacheConfiguration); } public RedisConfigCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, String... initialCacheNames) { super(cacheWriter, defaultCacheConfiguration, initialCacheNames); } public RedisConfigCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, boolean allowInFlightCacheCreation, String... initialCacheNames) { super(cacheWriter, defaultCacheConfiguration, allowInFlightCacheCreation, initialCacheNames); } public RedisConfigCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations) { super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations); } public RedisConfigCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations, boolean allowInFlightCacheCreation) { super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations, allowInFlightCacheCreation); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public void afterPropertiesSet() { String[] beanNames = applicationContext.getBeanNamesForType(Object.class); for (String beanName : beanNames) { final Class<?> clazz = applicationContext.getType(beanName); ReflectionUtils.doWithMethods(clazz, method -> { ReflectionUtils.makeAccessible(method); CacheExpire cacheExpire = AnnotationUtils.findAnnotation(method, CacheExpire.class); if (cacheExpire == null) { return; } Cacheable cacheable = AnnotationUtils.findAnnotation(method, Cacheable.class); if (cacheable != null) { cacheConfig(cacheable.cacheNames(), cacheExpire); return; } Caching caching = AnnotationUtils.findAnnotation(method, Caching.class); if (caching != null) { Cacheable[] cs = caching.cacheable(); if (cs.length > 0) { for (Cacheable c : cs) { if (c != null) { cacheConfig(c.cacheNames(), cacheExpire); } } } } else { CacheConfig cacheConfig = AnnotationUtils.findAnnotation(clazz, CacheConfig.class); if (cacheConfig != null) { cacheConfig(cacheConfig.cacheNames(), cacheExpire); } } }, method -> null != AnnotationUtils.findAnnotation(method, CacheExpire.class)); } super.afterPropertiesSet(); } private void cacheConfig(String[] cacheNames, CacheExpire cacheExpire) { RedisSerializer<String> redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>( Object.class); for (String cacheName : cacheNames) { if (StringUtils.isBlank(cacheName)) { continue; } long expire = cacheExpire.expire(); log.debug("cacheName: {}, expire: {}", cacheName, expire); if (expire >= -1) { // 缓存配置 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofSeconds(expire)) .disableCachingNullValues() .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)); cacheConfigurationMap.put(cacheName, config); } else { log.warn("{} use default expiration.", cacheName); } } } @Override protected Collection<RedisCache> loadCaches() { List<RedisCache> caches = new LinkedList<>(); for (Map.Entry<String, RedisCacheConfiguration> entry : cacheConfigurationMap.entrySet()) { caches.add(super.createRedisCache(entry.getKey(), entry.getValue())); } return caches; } }
package com.thunisoft.wfy.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface CacheExpire { long expire() default 0; }
---------------------------------------------------------------------
公用部分
package com.thunisoft.wfy.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; /** * redis配置类 * @version 2.14 * @author zhpj * @date 2020-12-08 */ @Slf4j @Configuration public class RedisConfig { @Bean @Primary public RedisCacheManager redisCacheManager(RedisConnectionFactory factory) { ObjectMapper om = new ObjectMapper(); RedisSerializer<String> redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>( Object.class); // 解决查询缓存转换异常的问题 om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); // 配置序列化(解决乱码的问题) RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofMillis(-1)) .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) .disableCachingNullValues(); RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(factory); /*return RedisConfigCacheManager.builder(factory) // .withInitialCacheConfigurations() .transactionAware() .build();*/ return new RedisConfigCacheManager(cacheWriter, config); } }