當(dāng)前位置:
首頁(yè) >
常用自定义注解
發(fā)布時(shí)間:2025/3/12
21
豆豆
導(dǎo)航
- 一、方法計(jì)時(shí)器
- 二、valid 參數(shù)校驗(yàn)的通用返回
- 三、接口訪問(wèn)頻次攔截(冪等)
一、方法計(jì)時(shí)器
注解類:MethodTimer
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface MethodTimer { }處理器(需要AOP和spring的支持):MethodTimerProcessor
@Slf4j @Component @Aspect public class MethodTimerProcessor {/*** 處理 @MethodTimer 注解*/@Around("@annotation(methodTimer)")public Object timerAround(ProceedingJoinPoint point, MethodTimer methodTimer) throws Throwable {long beginMills = System.currentTimeMillis();// process the methodObject result = point.proceed();log.info("{} 耗時(shí) : {} ms", point.getSignature(), System.currentTimeMillis() - beginMills);return result;} }使用方法:直接標(biāo)記在 Controller 的方法上。
二、valid 參數(shù)校驗(yàn)的通用返回
注解類:ValidCommonResp
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface ValidCommonResp { }處理器(aop+spring):ValidCommonRespProcessor
@Slf4j @Aspect @Component public class ValidCommonRespProcessor {/*** 處理 @ValidCommonResp 注解.* 注意,BindingResult是Spring validation的校驗(yàn)結(jié)果,* 當(dāng)參數(shù)傳入 BindingResult后,Spring MVC就不再控制校驗(yàn)* 結(jié)果的返回,如果不希望使用 @ValidCommonResp的校驗(yàn)結(jié)果* 封裝,請(qǐng)?jiān)诜椒ㄖ袑?shí)現(xiàn)校驗(yàn)結(jié)果的處理,二者任選其一。** @author mouhaotian*/@Around("@annotation(validCommonResp)")public Object aroundAdvice(ProceedingJoinPoint point, ValidCommonResp validCommonResp) throws Throwable {Object[] args = point.getArgs();for (Object arg : args) {if (arg instanceof BindingResult) {BindingResult bindingResult = (BindingResult) arg;if (bindingResult.hasErrors()) {FieldError fieldError = bindingResult.getFieldError();CommonResp commonResp = new CommonResp(CommonCode.FAIL,fieldError.getField() + fieldError.getDefaultMessage());return R.data(commonResp);}break;}}Object result = point.proceed(args);return result;} }使用方法:搭配 validation 注解、BindingResult 一起使用:
@PostMapping("/xxxx")@ValidCommonResppublic R submit(@Valid @RequestBody DoorzoneInfo doorzoneInfo, BindingResult result) {log.info("請(qǐng)求{}", doorzoneInfo);R commonResp = doorzoneInfoService.insertOrUpdDoorzoneInfo(doorzoneInfo);log.info("響應(yīng){}", commonResp);return commonResp;}好處:可以替代代碼塊中處理 BindingResult 的邏輯。
三、接口訪問(wèn)頻次攔截(冪等)
實(shí)現(xiàn)一個(gè)注解,當(dāng)controller中的方法收到請(qǐng)求后,在一定時(shí)間之內(nèi)(如10s內(nèi))拒絕接收相同參數(shù)的請(qǐng)求。即對(duì)后臺(tái)接口的訪問(wèn)增加了頻次限制,可以理解為一種不是特別標(biāo)準(zhǔn)的冪等。
注解 @Ide
/*** 冪等校驗(yàn)注解類*/ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Ide {/*** 關(guān)鍵key* key是本次請(qǐng)求中參數(shù)的鍵,* 重復(fù)請(qǐng)求的key取自header中的rid* 用來(lái)標(biāo)識(shí)這個(gè)請(qǐng)求的唯一性* 攔截器中會(huì)使用key從請(qǐng)求參數(shù)中獲取value** @return String*/String key() default "";/*** 自定義key的前綴用來(lái)區(qū)分業(yè)務(wù)*/String perFix();/*** 自定義key的超時(shí)時(shí)間(基于接口)*/String expireTime();/*** 禁止重復(fù)提交的模式* 默認(rèn)是全部使用*/IdeTypeEnum ideTypeEnum() default IdeTypeEnum.ALL; }AOP 橫切處理邏輯
/*** 注解執(zhí)行器 處理重復(fù)請(qǐng)求 和串行指定條件的請(qǐng)求* <p>* 兩種模式的攔截* 1.rid 是針對(duì)每一次請(qǐng)求的* 2.key+val 是針對(duì)相同參數(shù)請(qǐng)求* </p>* <p>* 另根據(jù)謝新的建議對(duì)所有參數(shù)進(jìn)行加密檢驗(yàn),提供思路,可以自行擴(kuò)展* DigestUtils.md5Hex(userId + "-" + request.getRequestURL().toString()+"-"+ JSON.toJSONString(request.getParameterMap()));* 或 DigestUtils.md5Hex(ip + "-" + request.getRequestURL().toString()+"-"+ JSON.toJSONString(request.getParameterMap()));* </p>*/ @Slf4j @Aspect @Component @RequiredArgsConstructor @ConditionalOnClass(RedisService.class) public class IdeAspect extends BaseAspect {private final ThreadLocal<String> PER_FIX_KEY = new ThreadLocal<String>();/*** 配置注解后 默認(rèn)開啟*/private final boolean enable = true;/*** request請(qǐng)求頭中的key*/private final static String HEADER_RID_KEY = "RID";/*** redis中鎖的key前綴*/private static final String REDIS_KEY_PREFIX = "RID:";/*** 鎖等待時(shí)長(zhǎng)*/private static int LOCK_WAIT_TIME = 10;private final RedisService redisService;@AutowiredIdeAspectConfig ideAspectConfig;@Pointcut("@annotation(cn.com.bmac.wolf.core.ide.annotation.Ide)")public void watchIde() {}@Before("watchIde()")public void doBefore(JoinPoint joinPoint) {Ide ide = getAnnotation(joinPoint, Ide.class);if (enable && null != ide) {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if (null == attributes) {throw new IdeException("請(qǐng)求數(shù)據(jù)為空");}HttpServletRequest request = attributes.getRequest();//根據(jù)配置文件中的超時(shí)時(shí)間賦值if (Func.isNotBlank(ideAspectConfig.getExpireTime())) {if(Func.isNumeric(ideAspectConfig.getExpireTime())){LOCK_WAIT_TIME = Integer.parseInt(ideAspectConfig.getExpireTime());}}//根據(jù)注解傳參賦值if(Func.isNotBlank(ide.expireTime())){LOCK_WAIT_TIME = Integer.parseInt(ide.expireTime());}//1.判斷模式if (ide.ideTypeEnum() == IdeTypeEnum.ALL || ide.ideTypeEnum() == IdeTypeEnum.RID) {//2.1.通過(guò)rid模式判斷是否屬于重復(fù)提交String rid = request.getHeader(HEADER_RID_KEY);if (Func.isNotBlank(rid)) {Boolean result = redisService.tryLock(REDIS_KEY_PREFIX + rid, LOCK_WAIT_TIME);if (!result) {throw new IdeException("命中RID重復(fù)請(qǐng)求");}log.debug("msg1=當(dāng)前請(qǐng)求已成功記錄,且標(biāo)記為0未處理,,{}={}", HEADER_RID_KEY, rid);} else {log.warn("msg1=header沒有rid,防重復(fù)提交功能失效,,remoteHost={}" + request.getRemoteHost());}}boolean isApiExpireTime = false;if (ide.ideTypeEnum() == IdeTypeEnum.ALL|| ide.ideTypeEnum() == IdeTypeEnum.KEY) {//2.2.通過(guò)自定義key模式判斷是否屬于重復(fù)提交String key = ide.key();if (Func.isNotBlank(key)) {String val = "";Object[] paramValues = joinPoint.getArgs();String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();//獲取自定義key的valueString[] keys = key.split("\\|");for(int i = 0; i < keys.length; i++){for (int j = 0; j < paramNames.length; j++) {//BindingResult 不能轉(zhuǎn)json,會(huì)導(dǎo)致線程報(bào)錯(cuò)終止if (paramValues[j] instanceof BindingResult) {continue;}String params = JSON.toJSONString(paramValues[j]);if (params.startsWith("{")) {//如果是對(duì)象//通過(guò)key獲取valueJSONObject jsonObject = JSON.parseObject(params);val = val + jsonObject.getString(keys[i]);} else if (keys[i].equals(paramNames[j])) {//如果是單個(gè)k=vval = val + params;} else {//如果自定義的key,在請(qǐng)求參數(shù)中沒有此參數(shù),說(shuō)明非法請(qǐng)求log.warn("自定義的key,在請(qǐng)求參數(shù)中沒有此參數(shù),防重復(fù)提交功能失效");}}}//判斷重復(fù)提交的條件String perFix = "";if (Func.isNotBlank(val)) {String[] perFixs = ide.perFix().split("\\|");int perFixsLength = perFixs.length;for(int i = 0; i < perFixs.length; i++){if(Func.isNotBlank(perFix)){perFix = perFix + ":" + perFixs[i];}else{perFix = perFixs[i];}}perFix = perFix + ":" + val;Boolean result = true;try {result = redisService.tryLock(perFix, LOCK_WAIT_TIME);} catch (Exception e) {log.error("獲取redis鎖發(fā)生異常", e);throw e;}if (!result) {String targetName = joinPoint.getTarget().getClass().getName();String methodName = joinPoint.getSignature().getName();log.error("msg1=不允許重復(fù)執(zhí)行,,key={},,targetName={},,methodName={}", perFix, targetName, methodName);throw new IdeException("不允許重復(fù)提交");}//存儲(chǔ)在當(dāng)前線程PER_FIX_KEY.set(perFix);log.info("msg1=當(dāng)前請(qǐng)求已成功鎖定:{}", perFix);} else {log.warn("自定義的key,在請(qǐng)求參數(shù)中value為空,防重復(fù)提交功能失效");}}}}}@After("watchIde()")public void doAfter(JoinPoint joinPoint) throws Throwable {try {Ide ide = getAnnotation(joinPoint, Ide.class);if (enable && null != ide) {if (ide.ideTypeEnum() == IdeTypeEnum.ALL|| ide.ideTypeEnum() == IdeTypeEnum.RID) {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();String rid = request.getHeader(HEADER_RID_KEY);if (Func.isNotBlank(rid)) {try {log.info("msg1=當(dāng)前請(qǐng)求已成功處理,,rid={}", rid);} catch (Exception e) {log.error("釋放redis鎖異常", e);}}PER_FIX_KEY.remove();}if (ide.ideTypeEnum() == IdeTypeEnum.ALL|| ide.ideTypeEnum() == IdeTypeEnum.KEY) {// 自定義keyString key = ide.key();if (Func.isNotBlank(key) && Func.isNotBlank(PER_FIX_KEY.get())) {try {log.info("msg1=當(dāng)前請(qǐng)求已成功釋放,,key={}", PER_FIX_KEY.get());PER_FIX_KEY.set(null);PER_FIX_KEY.remove();} catch (Exception e) {log.error("釋放redis鎖異常", e);}}}}} catch (Exception e) {log.error(e.getMessage(), e);}} }其他相關(guān)類
@Data @Component @ConfigurationProperties(prefix = "ide") public class IdeAspectConfig {/*** 過(guò)期時(shí)間 秒*/private String expireTime;} @Getter @AllArgsConstructor public enum IdeTypeEnum {/*** 0+1*/ALL(0, "ALL"),/*** ruid 是針對(duì)每一次請(qǐng)求的*/RID(1, "RID"),/*** key+val 是針對(duì)相同參數(shù)請(qǐng)求*/KEY(2, "KEY");private final Integer index;private final String title; }總結(jié)
- 上一篇: Linux版本Oracle工具,Linu
- 下一篇: iatf16949内审员_申请IATF1