Java增加自定義注解進行校驗入參詳解
背景
客戶使用我們系統(tǒng)的時候,查詢不帶任何查詢條件,查詢就返回全部數據,500多萬條數據啊,然后直接導出,數據量龐大,接口超時,這可苦了我們這些開發(fā)人員,一邊優(yōu)化,一邊挨噴。這么多數據就算導成功了,Excel也打不開呀。迫不得已,決定強制讓客戶至少傳入一個參數進行查詢來緩解服務器以及開發(fā)人員的壓力。
首先想到的,最簡單的,就是增加一個靜態(tài)方法,在每個方法入口調一下,來校驗以及拋出錯誤。但是轉念一想,更優(yōu)美的解決方案是在調用的方法上加一個注解,使用注解來完成這個功能,這豈不是很棒。
再一句話說下需求:
增加注解對入參進行校驗,保證至少有一個參數不為空,若是有時間參數,則起始時間和結束時間之間的距離不能超過30天。
接下來,Show Time
注解類
這里有三個參數,分別是三個參數名稱,起始時間參數名稱,結束時間參數名稱,需要校驗的參數名稱
@Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface VerifyParameters { /** * 起始時間參數名稱 */ String startTimeParamName() default "startTime"; /** * 結束時間參數名稱 */ String endTimeParamName() default "endTime"; /** * 需要校驗的參數名稱 */ String paramName() default ""; }
注解的Aspect類
這里貼一個完整的,目的是有的小伙伴想用的話,貼過去就能用。
@Component @Aspect public class VerifyParametersAspect { private static final Logger logger = LoggerFactory.getLogger(VerifyParametersAspect.class); /** * 切點 */ @Pointcut("@annotation(com.guava.mall.app.annotation.VerifyParameters)") public void serviceAspect() { } /** * service 方法前調用 * * @param joinPoint */ @Before("serviceAspect()") public void doBeforeService(JoinPoint joinPoint) { try { //獲取方法參數名 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); //獲取方法 Method method = signature.getMethod(); //獲取參數名 LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer(); String[] parameterNames = u.getParameterNames(method); Map<String, Object> params = new HashMap<>(8); params = getParamMap(joinPoint, method, parameterNames, params); //獲取注解 VerifyParameters verifyParameters = method.getAnnotation(VerifyParameters.class); //參數名 String paramName = verifyParameters.paramName(); Object o = params.get(paramName); ValidationUtils.validate(o == null, "參數不能為空"); ValidationUtils.validate(!atLeastOnePropertyNotNull(o), "請至少輸入一個查詢條件進行查詢和導出"); //開始時間和結束時間的參數名 String s = verifyParameters.startTimeParamName(); String e = verifyParameters.endTimeParamName(); Map<?, ?> map = JSONUtils.bean2Map(o); Object startTime = map.get(s); Object endTime = map.get(e); if (startTime != null || endTime != null) { ValidationUtils.validate(startTime == null || endTime == null, "開始時間和結束時間必須同時存在"); ValidationUtils.validate(Integer.parseInt(String.valueOf(endTime)) - Integer.parseInt(String.valueOf(startTime)) > 30 * 24 * 60 * 60, "時間間隔不能超過一個月"); } } catch (NumberFormatException ex) { logger.error(ex.getMessage(), ex); } } private Map<String, Object> getParamMap(JoinPoint joinPoint, Method method, String[] parameterNames, Map<String, Object> params) { int i = 0; if (parameterNames != null && parameterNames.length > 0) { for (String parameterName : parameterNames) { params.put(parameterName, joinPoint.getArgs()[i]); i++; } } return params; } public static boolean atLeastOnePropertyNotNull(Object obj) { for (Field field : obj.getClass().getDeclaredFields()) { //忽略serialVersionUID if ("serialVersionUID".equals(field.getName())) { continue; } field.setAccessible(true); try { if (field.get(obj) != null && !field.get(obj).toString().isEmpty()) { return true; } } catch (IllegalAccessException e) { e.printStackTrace(); } } return false; } /** * 方法后調用 */ @After("serviceAspect()") public void doAfterInService(JoinPoint joinPoint) { } }
然后,解釋一下
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
這是我們的第一行代碼,這里的joinPoint對象表示應用建議的程序執(zhí)行點,getSignature()方法則會返回正在執(zhí)行的方法的方法簽名,簽名里就包含了該方法名稱、返回類型和參數類型。然后再強制轉換成MethodSignature
對象,便于訪問方法相關的信息。
MethodSignature是一個對象,它表示正在執(zhí)行的方法的簽名,包括方法名稱、返回類型和參數類型。它是Spring AOP框架中的一個類,用于在切面中獲取方法的信息。
Method method = signature.getMethod();獲取方法
這個則是從MethodSignature中獲取到了正在執(zhí)行的方法信息。
LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
LocalVariableTableParameterNameDiscoverer是一個類,它可以用于在運行時獲取方法參數的名稱。它是Spring框架中的一個類,通常與反射一起使用,以便在運行時獲取有關方法參數的信息。
通過 String[] parameterNames = u.getParameterNames(method);
可以獲取到目前方法的入參名稱。
getParamMap();
方法則會返回一個key是參數名稱,value是參數本身的map對象。 我們可以從中取出我們需要的那個參數對象。
VerifyParameters verifyParameters = method.getAnnotation(VerifyParameters.class);
上邊這行代碼則會獲取到方法上注解的注解對象本身,和我們傳入的參數值,因為每個方法的入參不盡相同,里邊時間的字段也不盡相同,需要主動傳入來做處理。
這里再解釋下atLeastOnePropertyNotNull()
方法,這個方法的作用是判斷參數內的屬性是否至少有一個不為空,這里我們忽略了serialVersionUID
。
serialVersionUID是Java中的一個序列化機制,用于在反序列化期間驗證發(fā)送方和接收方的對象是否具有兼容的序列化版本。如果發(fā)送方和接收方的serialVersionUID不同,則反序列化將失敗。如果未指定serialVersionUID,則Java運行時將根據類的特定方面自動生成它。
之后的方法就很簡單了,拿出值根據我們的需要做處理即可
controller
再看看是如何使用的吧,添加@VerifyParameters
注解,傳入時間的屬性名稱,和需要校驗的參數名稱,這里傳入參數名稱的原因是,可能和我這里一樣還有其他的參數影響,而我們只想校驗我們需要的參數。
@ApiOperation(value = "查詢列表") @GetMapping("/test") @VerifyParameters(startTimeParamName = "beginTime", endTimeParamName = "endTime",paramName = "orderRequest") public Page<Map<String, Object>> findOrderTestList(Pageable pageable, ERPOrderRequest orderRequest) { log.info("模擬邏輯處理"); return null; }
這樣我們就實現了通過一個自定義注解對方法的入參進行了校驗,在取到入參和方法的各個值之后,我們其實可以做各種各樣的操作,各位小伙伴可以任意發(fā)揮。
到此這篇關于Java增加自定義注解進行校驗入參詳解的文章就介紹到這了,更多相關Java自定義注解校驗入參內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot+Mybatis+Vue 實現商品模塊的crud操作
這篇文章主要介紹了SpringBoot+Mybatis+Vue 實現商品模塊的crud操作,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-10-10SpringBoot使用@PostConstruct注解導入配置方式
這篇文章主要介紹了SpringBoot使用@PostConstruct注解導入配置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11java出現no XXX in java.library.path的解決及eclipse配
這篇文章主要介紹了java出現no XXX in java.library.path的解決及eclipse配置方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12Spring Bean Scope 有狀態(tài)的Bean與無狀態(tài)的Bean
這篇文章主要介紹了Spring Bean Scope 有狀態(tài)的Bean與無狀態(tài)的Bean,每個用戶有自己特有的一個實例,在用戶的生存期內,bean保持了用戶的信息,下面來了解有狀態(tài)和無狀態(tài)的區(qū)別吧2022-01-01