JAVA中的字段校驗(validation)
在開發(fā)業(yè)務時,不可避免的需要處理一些校驗, 如果是寫if-else這種代碼去校驗, 那會有一大段這樣的代碼。
不過還好有個校驗插件:javax.validation.validation-api,不過一般會引用hibernate的校驗組件:org.hibernate.hibernate-validator, 它已經引用了validation-api組件。
基礎校驗類型
JSR303 是一套JavaBean參數校驗的標準,它定義了很多常用的校驗注解,我們可以直接將這些注解加在我們JavaBean的屬性上面,就可以在需要校驗的時候進行校驗了。
注解如下:
Hibernate validator 在JSR303的基礎上對校驗注解進行了擴展,擴展注解如下:
寫個DEMO看看
校驗工具類:ValidatorUtils
import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.groups.Default; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; /** * Created by saleson on 2017/10/13. */ public class ValidatorUtils { private static Validator validator = Validation.buildDefaultValidatorFactory() .getValidator(); public static <T> Map<String, String> validate(T obj) { Map<String, StringBuilder> errorMap = new HashMap<>(); Set<ConstraintViolation<T>> set = validator.validate(obj, Default.class); if (set != null && set.size() > 0) { String property = null; for (ConstraintViolation<T> cv : set) { //這里循環(huán)獲取錯誤信息,可以自定義格式 property = cv.getPropertyPath().toString(); if (errorMap.get(property) != null) { errorMap.get(property).append("," + cv.getMessage()); } else { StringBuilder sb = new StringBuilder(); sb.append(cv.getMessage()); errorMap.put(property, sb); } } } return errorMap.entrySet().stream().collect(Collectors.toMap(k -> k.getKey(), v -> v.getValue().toString())); } }
DemoBean:
import com.fm.core.exceptions.ApiException; import org.hibernate.validator.constraints.NotEmpty; import com.fm.framework.api.ApiResultHelper; import com.fm.framework.json.Json; import com.fm.framework.utils.StringUtils; import com.fm.grantauth.domain.ValidateResult; import com.fm.grantauth.domain.dto.AuthorizationApplyDTO; import com.fm.grantauth.utils.ValidatorUtils; import org.junit.Test; import javax.validation.Valid; import java.util.Map; /** * Created by saleson on 2017/10/12. */ public class DataAuthValidator { private static final Logger log = LoggerFactory.getLogger(DataAuthValidator.class); @Override public ValidateResult validate(AuthorizationApplyDTO applyDTO) { String json = applyDTO.getContractParams(); if (StringUtils.isEmpty(json)) { throw new ApiException(ApiResultHelper.newParameterEmpty("contractParams字段不能為空")); } DataAuthContractParams params = Json.parseObject(json, DataAuthContractParams.class); Map<String, String> validMap = ValidatorUtils.validate(params); if (!validMap.isEmpty()) { log.warn(validMap.toString()); throw new ApiException(ApiResultHelper.newBusinessError(lackFieldMessage(validMap.keySet().toArray(new String[0])))); } return new ValidateResult(true); } public static class DataAuthContractParams { @NotEmpty private String businessName; @NotEmpty private String dataProvider; @NotEmpty private String personalDataName; @NotEmpty private String dataDemander; public String getBusinessName() { return businessName; } public void setBusinessName(String businessName) { this.businessName = businessName; } public String getDataProvider() { return dataProvider; } public void setDataProvider(String dataProvider) { this.dataProvider = dataProvider; } public String getPersonalDataName() { return personalDataName; } public void setPersonalDataName(String personalDataName) { this.personalDataName = personalDataName; } public String getDataDemander() { return dataDemander; } public void setDataDemander(String dataDemander) { this.dataDemander = dataDemander; } } @Test public void test() { AuthorizationApplyDTO applyDTO = new AuthorizationApplyDTO(); DataAuthContractParams params = new DataAuthContractParams(); params.setBusinessName("f"); params.setDataDemander("f"); params.setDataProvider(""); params.setPersonalDataName(""); applyDTO.setContractParams(Json.toJSONString(params)); ValidateResult result = new DataAuthValidator().validate(applyDTO); System.out.println(Json.toJSONString(result)); assert result.isSeccess(); } }
運行結果:
[main] WARN com.fm.grantauth.module.authorization.contact.DataAuthValidator - {dataProvider=不能為空, personalDataName=不能為空}
com.fm.core.exceptions.ApiException: 參數contractParams中缺少字段:dataProvider, personalDataName
自定義校驗規(guī)則(Validator)
自定義注解:
package com.fm.core.validation; import com.fm.core.validation.validator.NotEmptyValidator; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.ElementType.CONSTRUCTOR; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; /** * Created by saleson on 2017/5/31. */ @Retention(RetentionPolicy.RUNTIME) @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER}) @Documented @Constraint(validatedBy = {NotEmptyValidator.class}) public @interface NotEmpty { String message() default "參數不能為null或空字符串"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
校驗器(validator):
package com.fm.core.validation.validator; import com.fm.core.validation.NotEmpty; import com.fm.framework.utils.StringUtils; import org.apache.commons.collections.MapUtils; import javax.validation.ConstraintValidatorContext; import java.lang.reflect.Array; import java.util.Collection; import java.util.Map; /** * Created by saleson on 2017/5/27. */ public class NotEmptyValidator extends AbstractValidator<NotEmpty, Object> { @Override protected boolean validNull(ConstraintValidatorContext context) { return false; } @Override protected boolean valid(Object value, ConstraintValidatorContext context) { if (value instanceof String) { return StringUtils.isNotEmpty(value.toString()); } else if (value instanceof Collection) { return !org.springframework.util.CollectionUtils.isEmpty((Collection) value); } else if (value instanceof Map) { return MapUtils.isNotEmpty((Map) value); } else if (value.getClass().isArray()) { return Array.getLength(value) > 0; } return true; } }
將上個demo中引用的org.hibernate.validator.constraints.NotEmpty改為com.fm.core.validation.NotEmpty即可。
運行結果:
WARN com.fm.grantauth.module.authorization.contact.DataAuthValidator - {dataProvider=參數不能為null或空字符串, personalDataName=參數不能為null或空字符串}
com.fm.core.exceptions.ApiException: 參數contractParams中缺少字段:dataProvider, personalDataName
級聯校驗
校驗的對象中包含另一個需要校驗的對象時,則可以使用@javax.validation.Valid
import com.fm.core.exceptions.ApiException; import com.fm.core.validation.NotEmpty; import com.fm.framework.api.ApiResultHelper; import com.fm.framework.json.Json; import com.fm.framework.utils.StringUtils; import com.fm.grantauth.domain.ValidateResult; import com.fm.grantauth.domain.dto.AuthorizationApplyDTO; import com.fm.grantauth.utils.ValidatorUtils; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.validation.Valid; import java.util.Map; /** * Created by saleson on 2017/10/12. */ public class DataAuthValidator implements ContractParamsValidator { private static final Logger log = LoggerFactory.getLogger(DataAuthValidator.class); @Override public ValidateResult validate(AuthorizationApplyDTO applyDTO) { String json = applyDTO.getContractParams(); if (StringUtils.isEmpty(json)) { throw new ApiException(ApiResultHelper.newParameterEmpty("contractParams字段不能為空")); } DataAuthContractParams params = Json.parseObject(json, DataAuthContractParams.class); Map<String, String> validMap = ValidatorUtils.validate(params); if (!validMap.isEmpty()) { log.warn(validMap.toString()); throw new ApiException(ApiResultHelper.newBusinessError(lackFieldMessage(validMap.keySet().toArray(new String[0])))); } return new ValidateResult(true); } public static class DataAuthContractParams { @NotEmpty private String businessName; @NotEmpty private String dataProvider; @NotEmpty private String personalDataName; @NotEmpty private String dataDemander; // @NotEmpty @Valid private Re r; public String getBusinessName() { return businessName; } public void setBusinessName(String businessName) { this.businessName = businessName; } public String getDataProvider() { return dataProvider; } public void setDataProvider(String dataProvider) { this.dataProvider = dataProvider; } public String getPersonalDataName() { return personalDataName; } public void setPersonalDataName(String personalDataName) { this.personalDataName = personalDataName; } public String getDataDemander() { return dataDemander; } public void setDataDemander(String dataDemander) { this.dataDemander = dataDemander; } public Re getR() { return r; } public void setR(Re r) { this.r = r; } } public static class Re { @NotEmpty private String d; public String getD() { return d; } public void setD(String d) { this.d = d; } } @Test public void test() { AuthorizationApplyDTO applyDTO = new AuthorizationApplyDTO(); DataAuthContractParams params = new DataAuthContractParams(); params.setBusinessName("f"); params.setDataDemander("f"); params.setDataProvider(""); params.setPersonalDataName(""); Re r = new Re(); params.setR(r); applyDTO.setContractParams(Json.toJSONString(params)); ValidateResult result = new DataAuthValidator().validate(applyDTO); System.out.println(Json.toJSONString(result)); assert result.isSeccess(); } }
運行結果:
WARN com.fm.grantauth.module.authorization.contact.DataAuthValidator - {r.d=參數不能為null或空字符串, dataProvider=參數不能為null或空字符串, personalDataName=參數不能為null或空字符串}
com.fm.core.exceptions.ApiException: 參數contractParams中缺少字段:r.d, dataProvider, personalDataName
分組校驗
對同一個Model,我們在增加和修改時對參數的校驗也是不一樣的,這個時候我們就需要定義分組驗證。
com.fm.core.validation.NotEmpty#groups()就是用于分組校驗的
添加兩個用于分組校驗的接口:
public static interface GroupFirst { } public static interface GroupSecond { }
校驗改成:
public static class DataAuthContractParams { @NotEmpty private String businessName; @NotEmpty private String dataProvider; @NotEmpty(groups = GroupFirst.class) private String personalDataName; @NotEmpty(groups = {GroupFirst.class, GroupSecond.class}) private String dataDemander; // @NotEmpty @Valid private Re r; public String getBusinessName() { return businessName; } public void setBusinessName(String businessName) { this.businessName = businessName; } public String getDataProvider() { return dataProvider; } public void setDataProvider(String dataProvider) { this.dataProvider = dataProvider; } public String getPersonalDataName() { return personalDataName; } public void setPersonalDataName(String personalDataName) { this.personalDataName = personalDataName; } public String getDataDemander() { return dataDemander; } public void setDataDemander(String dataDemander) { this.dataDemander = dataDemander; } public Re getR() { return r; } public void setR(Re r) { this.r = r; } } public static class Re { @NotEmpty(groups = GroupSecond.class) private String d; public String getD() { return d; } public void setD(String d) { this.d = d; } }
修改ValidatorUtils
package com.fm.grantauth.utils; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.groups.Default; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; /** * Created by saleson on 2017/10/13. */ public class ValidatorUtils { private static Validator validator = Validation.buildDefaultValidatorFactory() .getValidator(); public static <T> Map<String, String> validate(T obj) { Set<ConstraintViolation<T>> set = validator.validate(obj, Default.class); return convertErrorMap(set); } public static <T> Map<String, String> validate(T obj, Class<?>... groups) { Set<ConstraintViolation<T>> set = validator.validate(obj, groups); return convertErrorMap(set); } private static <T> Map<String, String> convertErrorMap(Set<ConstraintViolation<T>> set) { Map<String, StringBuilder> errorMap = new HashMap<>(); if (set != null && set.size() > 0) { String property = null; for (ConstraintViolation<T> cv : set) { //這里循環(huán)獲取錯誤信息,可以自定義格式 property = cv.getPropertyPath().toString(); if (errorMap.get(property) != null) { errorMap.get(property).append("," + cv.getMessage()); } else { StringBuilder sb = new StringBuilder(); sb.append(cv.getMessage()); errorMap.put(property, sb); } } } return errorMap.entrySet().stream().collect(Collectors.toMap(k -> k.getKey(), v -> v.getValue().toString())); } }
測試GroupFirst.class:
@Test public void test() { DataAuthContractParams params = new DataAuthContractParams(); params.setBusinessName("f"); params.setDataDemander(""); params.setDataProvider("f"); params.setPersonalDataName(""); Re r = new Re(); params.setR(r); Map<String, String> validMap = ValidatorUtils.validate(params, GroupFirst.class); System.out.println("error is: " + validMap); }
運行結果:
error is: {dataDemander=參數不能為null或空字符串, personalDataName=參數不能為null或空字符串}
測試GroupSecond.class:
@Test public void test() { DataAuthContractParams params = new DataAuthContractParams(); params.setBusinessName("f"); params.setDataDemander(""); params.setDataProvider("f"); params.setPersonalDataName(""); Re r = new Re(); params.setR(r); Map<String, String> validMap = ValidatorUtils.validate(params, GroupSecond.class); System.out.println("error is: " + validMap); }
運行結果:
error is: {dataDemander=參數不能為null或空字符串, r.d=參數不能為null或空字符串}
測試GroupFirst.class+GroupSecond.class:
@Test public void test() { DataAuthContractParams params = new DataAuthContractParams(); params.setBusinessName("f"); params.setDataDemander(""); params.setDataProvider("f"); params.setPersonalDataName(""); Re r = new Re(); params.setR(r); Map<String, String> validMap = ValidatorUtils.validate(params, GroupFirst.class, GroupSecond.class); System.out.println("error is: " + validMap); }
運行結果:
error is: {dataDemander=參數不能為null或空字符串, r.d=參數不能為null或空字符串, personalDataName=參數不能為null或空字符串}
spring mvc上使用分組校驗:
@org.springframework.validation.annotation.Validated({GroupFirst.class, GroupSecond.class})
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
在Java中以及Spring環(huán)境下操作Redis的過程
文章介紹了在Java和Spring環(huán)境下操作Redis的基本方法,在Java環(huán)境下,使用Maven創(chuàng)建項目并導入Jedis依賴,通過配置端口轉發(fā)訪問Redis,文章總結了Redis的基本命令和類別,如String、list、hash、set和zset,感興趣的朋友跟隨小編一起看看吧2025-03-03Java使用Scala實現尾遞歸優(yōu)化來解決爆棧問題
Scala?作為一種多范式的編程語言,結合了面向對象和函數式編程的特性,在?Scala?中,尾遞歸?是通過編譯器優(yōu)化來防止棧溢出問題的,尾遞歸優(yōu)化是一種特殊的優(yōu)化方式,可以讓遞歸調用不使用新的棧幀,所以本文介紹了在Java項目中如何使用Scala實現尾遞歸優(yōu)化來解決爆棧問題2024-10-10