发布网友 发布时间:2022-04-21 19:00
共1个回答
热心网友 时间:2023-11-03 09:08
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// 配置序列化
// 统一默认配置,TTL为60秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(60));
// 针对不同key可以个性化设置
Set<String> cacheNames = new HashSet<>();
cacheNames.add("account");
cacheNames.add("post");
// 对每个缓存空间应用不同的配置
Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
configMap.put("account", config.entryTtl(Duration.ofSeconds(30)));
configMap.put("post", config);
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(config)
.initialCacheNames(cacheNames)
.withInitialCacheConfigurations(configMap)
.build();
}
自定义每个缓存的TTL和自动刷新
上面的设置虽然可以提前设定每个cacheName的ttl,但是不够细,虽然Cacheable的sync可以防止缓存击穿,但是无法防止缓存雪崩。
我们可以使用AOP来完成自动刷新和自定义TTL。
代码写的比较糙,只是个例子,可以自己修改。
自己写TTL注解
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface CacheTTL {
@AliasFor("ttl")
long value() default 60;
@AliasFor("value")
long ttl() default 60;
long preExpireRefresh() default 10;
}
AOP实现
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
//这里必须要比Cacheable的Aspect优先级要高
@Order(value=1)
@Aspect
@Component
public class CustomCacheAspect {
private final RedisTemplate<String,Object> redisTemplate;
private final SimpleKeyGenerator keyGenerator = new SimpleKeyGenerator();
private final AtomicInteger asyncLock = new AtomicInteger(0);
public CustomCacheAspect(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@After(value="execution(* *..*.*(..))")
public void after(JoinPoint point)
{
Object target = point.getTarget();
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
try {
if (method.isAnnotationPresent(CacheTTL.class) && method.isAnnotationPresent(Cacheable.class)) {
CacheTTL ttlData = AnnotationUtils.getAnnotation(method, CacheTTL.class);
Cacheable cacheAbleData = AnnotationUtils.getAnnotation(method, Cacheable.class);
long ttl = ttlData.ttl();
long preExpireRefresh = ttlData.preExpireRefresh();
String[] cacheNames = cacheAbleData.cacheNames();
//默认的keyGenerator生成,如果自定义了自己改一下
Object key = keyGenerator.generate(target, method, point.getArgs());
updateExpire(cacheNames,key,preExpireRefresh,ttl);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void updateExpire(String[] cacheNames,Object key,long preExpireRefresh,long ttl){
if (asyncLock.compareAndSet(0,1)){
Arrays.stream(cacheNames).parallel().forEach(cacheName->{
cacheName = cacheName + "::" + key;
long expire = redisTemplate.getExpire(cacheName,TimeUnit.SECONDS);
if (expire>0 && expire<=preExpireRefresh || expire > ttl || expire == -1){
redisTemplate.expire(cacheName, ttl, TimeUnit.SECONDS);
}
});
asyncLock.set(0);
}
}
}
使用
@Cacheable(cacheNames = "account",key="#id",sync = true)
@CacheTTL(ttl = 60,preExpireRefresh = 10)
public Account findAccountByID(int id) {
return accountDao.findAccountByID(id);
}