網站首頁 編程語言 正文
基于 spring aop 常規應用場景多是用于日志記錄以及實現 redis 分布式鎖,在 github 中也有項目是把它拿來當作緩存的異常捕捉。從而避免影響實際業務的開發;在某天,筆者有個業務開發是給某個服務模塊增加 redis 緩存。增加緩存就會涉及 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);
}
}
}
原文鏈接:https://blog.csdn.net/java_best_LJF/article/details/118327818
相關推薦
- 2021-12-14 HTML+jQuery實現簡單的登錄頁面_jquery
- 2022-10-16 Django完整增刪改查系統實例代碼_python
- 2023-07-31 element中el-input無法輸入
- 2022-08-03 C++編程語言中賦值運算符重載函數(operator=)的使用_C 語言
- 2022-07-22 C語言中字符串詳解
- 2022-05-13 C++ 使用Poco庫實現XML的讀取和寫入
- 2022-10-23 Redis?如何清空所有數據_Redis
- 2022-09-09 Oracle存儲過程與函數的詳細使用教程_oracle
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支