这篇文章主要介绍“基于Spring Aop环绕通知如何实现Redis缓存双删功能”,在日常操作中,相信很多人在基于Spring Aop环绕通知如何实现Redis缓存双删功能问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”基于Spring Aop环绕通知如何实现Redis缓存双删功能”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
结构示意图:
自定义注解 RedisDelByDbUpdate
@Repeatable 表示允许在同一个地方上使用相同的注解,没有该注解时贴相同注解会报错
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(RedisDelByDbUpdateList.class)
public @interface RedisDelByDbUpdate {
/**
* 具体操作得缓存前缀
*/
String redisPrefix() default "";
/**
* 具体的缓存名称,为空则删除 redisPrefix 下的所有缓存
*/
String fieldName() default "";
}
自定义注解 RedisDelByUpdateList
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisDelByDbUpdateList {
RedisDelByDbUpdate[] value();
}
具体实现 redis 双删逻辑
@Aspect
@Component
@Slf4j
public class RedisDelAspect_bak {
//环绕增强
@Autowired
private RedisUtil redis;
@Pointcut("@annotation(com.cili.baseserver.aop.annotation.RedisDelByDbUpdate) " +
"|| @annotation(com.cili.baseserver.aop.annotation.RedisDelByDbUpdateList)")
public void pointCut() {
}
// 线程池定长设置:最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(26);
/**
* 环绕增强
*
* @param point
* @return
* @throws Throwable
*/
@Around("pointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
RedisDelByDbUpdate redisDelByDbUpdate = method.getAnnotation(RedisDelByDbUpdate.class);
if (redisDelByDbUpdate != null) {
return singleRedisDel(redisDelByDbUpdate, point);
}
RedisDelByDbUpdateList updateList = method.getAnnotation(RedisDelByDbUpdateList.class);
return arraysRedisDel(updateList, point);
}
private Object singleRedisDel(RedisDelByDbUpdate redisDelByDbUpdate, ProceedingJoinPoint point) throws Throwable {
return handle(redisDelByDbUpdate, point, new IObjectCallback<Object>() {
public <T> T callback() throws Throwable {
return (T) point.proceed();
}
});
}
private Object arraysRedisDel(RedisDelByDbUpdateList updateList, ProceedingJoinPoint point) throws Throwable {
RedisDelByDbUpdate[] redisDelByDbUpdates = updateList.value();
for (int i = 0; i < redisDelByDbUpdates.length; i++) {
boolean flag = i == redisDelByDbUpdates.length - 1 ? true : false;
Object obj = handle(redisDelByDbUpdates[i], point, new IObjectCallback<Object>() {
public <T> T callback() throws Throwable {
return flag ? (T) point.proceed() : null;
}
});
if (flag) return obj;
}
return null;
}
private Object handle(RedisDelByDbUpdate redisDelByDbUpdate, ProceedingJoinPoint point,
IObjectCallback<Object> object) throws Throwable {
String prefix = redisDelByDbUpdate.redisPrefix();
String fieldName = redisDelByDbUpdate.fieldName();
if (ValueUtil.isEmpty(prefix)) {
log.info("redis缓存前缀不能为空");
throw new BizException(BaseResponseCode.SYSTEM_BUSY);
}
Object arg = point.getArgs()[0];
String key = "";
String[] redisKeys = null;
if (ValueUtil.isNotEmpty(fieldName)) {
if (arg instanceof ArrayList) {
redisKeys = ((ArrayList<?>) arg).stream().map(item -> prefix + item).toArray(String[]::new);
} else {
Map<String, Object> map = (Map<String, Object>) JsonUtil.toMap(JsonUtil.toJSON(point.getArgs()[0]));
key = map.get(fieldName).toString();
}
} else {
// 获取所有该前缀下缓存
Set<String> keys = redis.keys(prefix + "*");
redisKeys = keys.stream().toArray(String[]::new);
}
// 删除缓存
String redisKey = prefix + key;
delete(redisKey, redisKeys);
Object result = object.callback();
// 延时删除
try {
String[] finalRedisKeys = redisKeys;
threadPool.schedule(new Runnable() {
@Override
public void run() {
delete(redisKey, finalRedisKeys);
}
}, 500, TimeUnit.MILLISECONDS);
} catch (Exception e) {
log.error("线程池延时删除缓存失败:{}", redisKey);
}
return result;
}
private void delete(String redisKey, String[] redisKeys) {
if (ValueUtil.isEmpty(redisKeys)) {
redis.delete(redisKey);
return;
}
redis.del(redisKeys);
}
}
注解使用示例
public class Test {
@Override
@RedisDelByDbUpdate(redisPrefix = RedisConstant.BANNER, fieldName = "ids")
@RedisDelByDbUpdate(redisPrefix = RedisConstant.BANNER_LIST)
public void batchDeleted(List<String> ids) throws BizException {
if (CollectionUtils.isEmpty(ids)) {
log.info("banner ID 不能为空");
throw new BizException(BaseResponseCode.PARAM_IS_NOT_NULL);
}
try {
bannerMapper.batchDeleted(ids);
} catch (Exception e) {
log.error("==>批量删除Banner错误:{}<==", e);
throw new BizException(BaseResponseCode.SYSTEM_BUSY);
}
}
}
到此,关于“基于Spring Aop环绕通知如何实现Redis缓存双删功能”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。