基于 Spring Aop 环绕通知实现 Redis 缓存双删功能(示例代码)
短信预约 -IT技能 免费直播动态提醒
基于 spring aop 常规应用场景多是用于日志记录以及实现 Redis 分布式锁,在 github 中也有项目是把它拿来当作缓存的异常捕捉。从而避免影响实际业务的开发;在某天,笔者有个业务开发是给某个服务模块增加 redis 缓存。增加缓存就会涉及 redis 删除。所以笔者就在思考是不是可以用环绕通知的方式来进行实现
代码实现
结构示意图:
自定义注解 RedisDelByDbUpdate
@Repeatable 表示允许在同一个地方上使用相同的注解,没有该注解时贴相同注解会报错
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(RedisDelByDbUpdateList.class)
public @interface RedisDelByDbUpdate {
String redisPrefix() default "";
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);
@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 缓存双删的文章就介绍到这了,更多相关Spring Aop Redis 缓存双删内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341