基于AOP和Hibernate Validate框架的后臺驗證
整體思路
- 對Controller層的方法做切面攔截
- 驗證入參是否使用
@DoValid
注解修飾 - 在切面類中調用Hibernate Validate框架依次對入參進行驗證
- 全部驗證合法,執(zhí)行原方法體击吱,若驗證不通過淋淀,動態(tài)判斷返回類型,并構造返回結果
Demo
- 新增
- 查詢
常用注解
Annotation | Value | Scope |
---|---|---|
@NotNull | 不為null | 字段或屬性 |
@NotEmpty | 不為null同時也不為空 | 字段或屬性,String,Collection,Map,數組 |
@NotBlank | 字符串不為null,并且不是空字符串(忽略前后空白字符) | 字段或屬性 |
@Pattern | 是否匹配正則 | String |
... | ... | ... |
自定義注解
- 定義注解
- 定義驗證類
如何與系統(tǒng)整合
- pom.xml添加如下依賴:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.3.3.Final</version>
</dependency>
- 在spirng.xml中添加如下配置:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
</bean>
或者使用@Bean
注解覆醇,注入該Bean
- 在pom.xml中引入vb依賴或者將如下文件拷貝到對應系統(tǒng)中
DoValid.java
和DoValidAspect.java
/*
* Copyright (c) 2016, www.vnetoo.com. All rights reserved.
*/
package com.vnetoo.vcomponent.validate;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 參數驗證注解
* @date 2016年11月25日 下午4:48:53
* @author zhaoj
* @since V2.0.0
*/
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DoValid {
//如需要引入分組朵纷,加入如下這句即可,代碼中使用@DoValid({GroupA.class})
//Class<?>[] value() default {};
}
/*
* Copyright (c) 2016, www.vnetoo.com. All rights reserved.
*/
package com.vnetoo.vcomponent.validate;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Set;
import javax.validation.ConstraintViolation;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import com.vnetoo.common.AppContext;
import com.vnetoo.common.enums.Result;
import com.vnetoo.common.vo.AjaxResponse;
import com.vnetoo.common.vo.CommonResponse;
/**
* 表單后臺驗證切面
*
* @date 2016年11月25日 上午10:12:21
* @author zhaoj
* @since V2.0.0
*/
@Aspect
@Component
public class DoValidAspect {
/**
* 驗證類的結尾
*
* @date 2016年11月25日 下午4:51:41
* @author zhaoj
* @since V2.0.0
*/
public static final String VALIDATOR_SUFFIX = "Validator";
/**
* 定義切面
*
* @date 2016年11月25日 上午10:12:02
* @author zhaoj
* @since V2.0.0
*/
@Pointcut("execution(public * com.vnetoo..*Controller.*(..))")
public void validate() {
}
/**
* 切面方法體環(huán)繞
*
* @date 2016年11月25日 上午10:11:33
* @author zhaoj
* @since V2.0.0
* @param pjp
* @return
* @throws Throwable
*/
@Around(value = "validate()")
private Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
/*
* 算法: 1.取得方法體中的參數注解二維數組 2.逐一遍歷參數中的注解永脓,并對帶有帶有 @Valid 注解的參數做驗證處理
*
* 驗證處理: 1.調用當前Controller中的validate()
* 2.如果返回的CommonResponse.Result!=Result.Success袍辞,驗證返回類型
* 2.1 JSON(主要用于添加、編輯):判斷Controller是否有@RestController 或者 方法上加了@ResponseBody常摧,返回帶錯誤信息的JSON
* 2.2 View(主要用于查詢): 判斷Controller是否返回值為ModelAndView搅吁,返回帶錯誤信息的錯誤頁面
*
* 其中,錯誤信息在CommonResponse.msg中
* 3.若所有驗證都通過了落午,則可以繼續(xù)執(zhí)行方法
*/
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
//[參數][注解]二維數組
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
if (parameterAnnotations != null && parameterAnnotations.length != 0) {
for (int i = 0; i != parameterAnnotations.length; ++i) {
Annotation[] param = parameterAnnotations[i];
for (int j = 0; j != param.length; ++j) {
Annotation annotation = param[j];
if (annotation instanceof DoValid) {
// 參數中有驗證注解
Object bo = pjp.getArgs()[i];
/*******************單個入參驗證開始*******************/
CommonResponse validator = validate(bo);
//分組支持
//CommonResponse validator = validate(bo,((DoValid) annotation).value());
if (validator.getResult() != Result.SUCCESS) {
//驗證不通過,驗證返回方式
if(isJsonResponse(pjp)){
//返回JSON
return new AjaxResponse(validator .getResult(), AppContext.token(), validator .getMsg());
}else{
//返回View
return new ModelAndView("errorMsg").addObject("errMsg", validator .getMsg());
}
}
/*******************單個入參驗證結束*******************/
}
}
}
}
//所有入參驗證完畢谎懦、已做字段過濾,且均合法
//代碼若能執(zhí)行到此處溃斋,則說明驗證通過界拦,則執(zhí)行Controller層中的方法體
return pjp.proceed();
}
/**
* 判斷是否是返回JSON的請求
* @date 2016年12月1日 下午4:45:50
* @author zhaoj
* @since V2.0.0
* @param pjp
* @return
*/
public static boolean isJsonResponse(ProceedingJoinPoint pjp){
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
Annotation[] methodAnnotations = method.getAnnotations();
for(Annotation tmp : methodAnnotations){
if(tmp instanceof ResponseBody){
return true;
}
}
Annotation[] classAnnotations = method.getClass().getAnnotations();
for(Annotation tmp : classAnnotations){
if(tmp instanceof RestController){
return true;
}
}
return false;
}
/**
* 參數驗證
* @date 2016年12月1日 下午4:46:00
* @author zhaoj
* @since V2.0.0
* @param bo
* @return
*/
public static CommonResponse validate(Object bo) {
Set<ConstraintViolation<Object>> set = ((LocalValidatorFactoryBean) AppContext.getBean("validator")).getValidator().validate(bo);
//分組驗證支持
//Set<ConstraintViolation<Object>> set2 = ((LocalValidatorFactoryBean) AppContext.getBean("validator")).getValidator().validate(bo, ((DoValid) annotation).value());
if (set != null && !set.isEmpty()) {
String msg = "";
Iterator<ConstraintViolation<Object>> iterator = set.iterator();
while (iterator.hasNext()) {
String singleMsg = iterator.next().getMessage();
if (StringUtils.isNotEmpty(singleMsg)) {
msg += singleMsg + ",";
}
}
if (StringUtils.isNotEmpty(msg.toString())) {
msg = msg.substring(0, msg.toString().length() - 1);
}
return new CommonResponse(Result.ERROR, msg);
}
return new CommonResponse(Result.SUCCESS, null);
}
}
如果需要指定分組的形式,請查看鏈接:分組驗證梗劫,然后對上述代碼略加修改即可寞奸。
- 不采用分組策略:建
XXXCreateForm
、XXXEditForm
在跳、XXXSearchForm
類并在From的屬性上加驗證注解即可
采用分組策略:建立分組接口枪萄,在BO的屬性上加驗證注解,并指定分組猫妙,詳見:分組驗證 - 在需要驗證的入參中加上
@DoValid
注解修飾即可瓷翻,如:
/**
* 添加保存
* @date 2016-12-09 15:19:51
* @author zhaoj
* @since V1.0.3
* @param bo
* @return
*/
@CheckToken
@ResponseBody
@Authorization
@LogMark(memo="添加保存")
@RequestMapping(value = "/insert")
public AjaxResponse insert(@DoValid ExamUserCreateForm form){
try {
ExamUser bo = new ExamUser();
PropertyUtils.copyProperties(bo, form);
getService().insert(bo);
return new AjaxResponse();
} catch (Exception e) {
return new AjaxResponse(Result.ERROR, AppContext.token(), e.getMessage());
}
}