Spring AOP 通用入參校驗終極版

入參校驗一直是程序中一塊雞肋,食之無味卻又不得不吃。經過幾個版本變更,本次項目上線筆者終於將入參校驗應用了稍微高級一點的寫法。

基調:hibernate.validator
實現-低配版

1、引入pom

    <dependency>
     
        <groupId>org.hibernate</groupId>
     
        <artifactId>hibernate-validator</artifactId>
     
        <version>5.0.2.Final</version>
     
    </dependency>

2、封裝校驗類

    public class ValidationUtils {
        private static ILog logger = LogFactory.getLogger(ValidationUtils.class);
     
        /**
         * 使用hibernate的註解來進行驗證
         */
        private static Validator validator = Validation.byProvider(HibernateValidator.class).configure().failFast(true).buildValidatorFactory().getValidator();
     
        public static <T> void validate(T obj) {
            Set<ConstraintViolation<T>> constraintViolations = validator.validate(obj);
            // 拋出檢驗異常
            if (constraintViolations.size() > 0) {
                logger.warn("param invalidate fail :{}", constraintViolations.iterator().next().getMessage());
                System.out.println(constraintViolations.iterator().next().getMessage());
            }
        }
     
    }

3、應用

如對TradeDto進行校驗則在TradeDto中必傳的參數上增加@NotNull註解,並定義返回msg

    public class TradeDto {
        @NotNull(message = "訂單ID爲空")
        private Long orderId;
     
        @NotNull(message = "業務線ID爲空")
        private Long businessId;
    .......
    }

方法處由原挨個判斷改爲調用封裝類校驗。

        public Result<TradeDto> createTrade(TradeDto tradeDto) {
            if (tradeDto == null || tradeDto.getOrderId() == null || tradeDto.getBusinessId() == null) {
                return ResultWrapper.fail(ErrorCode.PARAMETER_CANNOT_NULL);
            }
    替換爲:
     
        ValidationUtils.validate(tradeDto);

4、結果

入參tradeDto中orderId爲空時,返回“訂單ID爲空”的msg

******************************************************* 方案一完畢***********************************************************************

如果就這麼結束了 未免也太low了。

方案一缺點:

1、每次入參校驗都要寫一段:ValidationUtils.....(簡單但不太高級)

2、ValidationUtils封裝類只校驗單個dto,那如果入參有Long+Dto等組合情況呢?難不成調兩遍ValidationUtils?(粗暴又低級)

3、validate方法返回個void,那調用處是接着執行呢,還是return呢?(返回值應結合業務邏輯)
實現-升級版

基調:AOP+註解;方法可變參數;返回值封裝

1、註解類封裝

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Valid {
    }

2、切面類封裝

    @Aspect
    @Component
    @Order(2)
    public class ValidAspect {
        /**
         * 獲取參數進行入參校驗
         *
         * @param joinPoint
         * @return
         * @throws Throwable
         */
        @Around("execution(* com.trade.component..*.*(..)) && @annotation(com.trade.aspect.Valid))")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            Object[] args = joinPoint.getArgs();
            Result validate = ValidationUtils.validate(args);
            if (validate.isSuccess()) {
                return joinPoint.proceed();
            }
            return validate;
        }
    }

注:註解order(1)後面解釋

3、校驗方法優化(可變參數+業務有意義返回值)

        public static <T> Result validate(T... obj) {
            for (int i = 0; i < obj.length; i++) {
                if (obj[i] == null) {
                    return ResultWrapper.fail(ErrorCode.PARAMETER_IS_NULL);
                }
                Set<ConstraintViolation<T>> constraintViolations = validator.validate(obj[i]);
                if (constraintViolations.size() <= 0) {
                    continue;
                } else {
                    logger.warn("param invalidate fail :{}", constraintViolations.iterator().next().getMessage());
                    System.out.println(constraintViolations.iterator().next().getMessage());
                    return ResultWrapper.fail(constraintViolations.iterator().next().getMessage());
                }
            }
            return ResultWrapper.success();
        }

4、應用

        @Valid
        @Log
        @Override
        public Result<TradeDto> createTrade(TradeDto tradeDto) {..}

5、結果

添加@Valid註解執行方法入參校驗,參數類型支持多個(基礎類型+Dto組合傳參)。

AOP執行順序控制

之前博客寫過使用AOP進行log入參出參統一日誌輸出的實現,如上createTrade方法,已有兩個zidi自定義AOP作用於gaif該方法,那如何控制AOP執行順序呢?@Order(int)註解就是這個作用。int值越小該切點yuex越先執行。所以Log切點xian項目應用@Order(1),先進行入參打印,而後執行參數校驗@Valid。具體其他控制方法總結如下:

1、@Order(最直觀簡單)

2、配置文件(避免有些面試官刨根問底,知道即可)

    <aop:config expose-proxy="true">  
        <aop:aspect ref="aopBean" order="0">    
            <aop:pointcut id="testPointcut" expression="@annotation(xxx.xxx.xxx.annotation.xxx)"/>    
            <aop:around pointcut-ref="testPointcut" method="doAround" />    
            </aop:aspect>    
    </aop:config>  

3、顯示實現Order接口

    @Component
    @Aspect
    public class MessageQueueAopAspect1 implements Ordered{
        @Override
        public int getOrder() {
            return 2;
        }
        
    }

原文:https://blog.csdn.net/Daybreak1209/article/details/82747672
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章