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