hibernate-validator如何使用校驗(yàn)框架
一、前言
高效、合理的使用hibernate-validator校驗(yàn)框架可以提高程序的可讀性,以及減少不必要的代碼邏輯。接下來(lái)會(huì)介紹一下常用一些使用方式。
二、常用注解說(shuō)明
| 限制 | 說(shuō)明 |
| @Null | 限制只能為null |
| @NotNull | 限制必須不為null |
| @AssertFalse | 限制必須為false |
| @AssertTrue | 限制必須為true |
| @DecimalMax(value) | 限制必須為一個(gè)不大于指定值的數(shù)字 |
| @DecimalMin(value) | 限制必須為一個(gè)不小于指定值的數(shù)字 |
| @Digits(integer,fraction) | 限制必須為一個(gè)小數(shù),且整數(shù)部分的位數(shù)不能超過(guò)integer,小數(shù)部分的位數(shù)不能超過(guò)fraction |
| @Future | 限制必須是一個(gè)將來(lái)的日期 |
| @Max(value) | 限制必須為一個(gè)不大于指定值的數(shù)字 |
| @Min(value) | 限制必須為一個(gè)不小于指定值的數(shù)字 |
| @Past | 限制必須是一個(gè)過(guò)去的日期 |
| @Pattern(value) | 限制必須符合指定的正則表達(dá)式 |
| @Size(max,min) | 限制字符長(zhǎng)度必須在min到max之間 |
| @Past | 驗(yàn)證注解的元素值(日期類型)比當(dāng)前時(shí)間早 |
| @NotEmpty | 驗(yàn)證注解的元素值不為null且不為空(字符串長(zhǎng)度不為0、集合大小不為0) |
| @NotBlank | 驗(yàn)證注解的元素值不為空(不為null、去除首位空格后長(zhǎng)度為0),不同于@NotEmpty,@NotBlank只應(yīng)用于字符串且在比較時(shí)會(huì)去除字符串的空格 |
| 驗(yàn)證注解的元素值是Email,也可以通過(guò)正則表達(dá)式和flag指定自定義的email格式 |
三、定義校驗(yàn)分組
public class ValidateGroup {
public interface FirstGroup {
}
public interface SecondeGroup {
}
public interface ThirdGroup {
}
}四、定義校驗(yàn)Bean
@Validated
@GroupSequence({ValidateGroup.FirstGroup.class, BaseMessageRequestBean.class})
public class BaseMessageRequestBean {
//渠道類型
@NotNull(message = "channelType為NULL", groups = ValidateGroup.FirstGroup.class)
private String channelType;
//消息(模板消息或者普通消息)
@NotNull(message = "data為NUll", groups = ValidateGroup.FirstGroup.class)
@Valid
private Object data;
//業(yè)務(wù)類型
@NotNull(message = "bizType為NULL", groups = ValidateGroup.FirstGroup.class)
private String bizType;
//消息推送對(duì)象
@NotBlank(message = "toUser為BLANK", groups = ValidateGroup.FirstGroup.class)
private String toUser;
private long createTime = Instant.now().getEpochSecond();
......
}請(qǐng)自行參考:@Validated和@Valid區(qū)別
五、validator基本使用
@RestController
public class TestValidatorController {
@RequestMapping("/test/validator")
public void test(@Validated BaseMessageRequestBean bean){
...
}
}這種使用方式有一個(gè)弊端,不能自定義返回異常。spring如果驗(yàn)證失敗,則直接拋出異常,一般不可控。
六、借助BindingResult
@RestController
public class TestValidatorController {
@RequestMapping("/test/validator")
public void test(@Validated BaseMessageRequestBean bean, BindingResult result){
result.getAllErrors();
...
}
}如果方法中有BindingResult類型的參數(shù),spring校驗(yàn)完成之后會(huì)將校驗(yàn)結(jié)果傳給這個(gè)參數(shù)。通過(guò)BindingResult控制程序拋出自定義類型的異常或者返回不同結(jié)果。
七、全局?jǐn)r截校驗(yàn)器
當(dāng)然了,需要在借助BindingResult的前提下...
@Aspect
@Component
public class ControllerValidatorAspect {
@Around("execution(* com.*.controller..*.*(..)) && args(..,result)")
public Object doAround(ProceedingJoinPoint pjp, result result) {
result.getFieldErrors();
...
}
}這種方式可以減少controller層校驗(yàn)的代碼,校驗(yàn)邏輯統(tǒng)一處理,更高效。
八、借助ValidatorUtils工具類
@Bean
public Validator validator() {
return new LocalValidatorFactoryBean();
}LocalValidatorFactoryBean官方示意
LocalValidatorFactoryBean是Spring應(yīng)用程序上下文中javax.validation(JSR-303)設(shè)置的中心類:它引導(dǎo)javax.validation.ValidationFactory并通過(guò)Spring Validator接口以及JSR-303 Validator接口和ValidatorFactory公開它。界面本身。通過(guò)Spring或JSR-303 Validator接口與該bean的實(shí)例進(jìn)行通信時(shí),您將與底層ValidatorFactory的默認(rèn)Validator進(jìn)行通信。這非常方便,因?yàn)槟槐卦诠S執(zhí)行另一個(gè)調(diào)用,假設(shè)您幾乎總是會(huì)使用默認(rèn)的Validator。這也可以直接注入Validator類型的任何目標(biāo)依賴項(xiàng)!從Spring 5.0開始,這個(gè)類需要Bean Validation 1.1+,特別支持Hibernate Validator 5.x(參見setValidationMessageSource(org.springframework.context.MessageSource))。這個(gè)類也與Bean Validation 2.0和Hibernate Validator 6.0運(yùn)行時(shí)兼容,有一個(gè)特別說(shuō)明:如果你想調(diào)用BV 2.0的getClockProvider()方法,通過(guò)#unwrap(ValidatorFactory.class)獲取本機(jī)ValidatorFactory,在那里調(diào)用返回的本機(jī)引用上的getClockProvider()方法。Spring的MVC配置命名空間也使用此類,如果存在javax.validation API但未配置顯式Validator。
public class ValidatorUtils implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ValidatorUtils.validator = (Validator) applicationContext.getBean("validator");
}
private static Validator validator;
public static Optional<String> validateResultProcess(Object obj) {
Set<ConstraintViolation<Object>> results = validator.validate(obj);
if (CollectionUtils.isEmpty(results)) {
return Optional.empty();
}
StringBuilder sb = new StringBuilder();
for (Iterator<ConstraintViolation<Object>> iterator = results.iterator(); iterator.hasNext(); ) {
sb.append(iterator.next().getMessage());
if (iterator.hasNext()) {
sb.append(" ,");
}
}
return Optional.of(sb.toString());
}
}為什么要使用這個(gè)工具類呢?
1、controller方法中不用加入BindingResult參數(shù)
2、controller方法中需要校驗(yàn)的參數(shù)也不需要加入@Valid或者@Validated注解
怎么樣是不是又省去了好多代碼,開不開心。
具體使用,在controller方法或者全局?jǐn)r截校驗(yàn)器中調(diào)用ValidatorUtils.validateResultProcess(需要校驗(yàn)的Bean) 直接獲取校驗(yàn)的結(jié)果。
請(qǐng)參考更多功能的ValidatorUtils工具類。
九、自定義校驗(yàn)器
定義一個(gè)MessageRequestBean,繼承BaseMessageRequestBean,signature字段需要我們自定義校驗(yàn)邏輯。
@Validated
@GroupSequence({ValidateGroup.FirstGroup.class, ValidateGroup.SecondeGroup.class, MessageRequestBean.class})
@LogicValidate(groups = ValidateGroup.SecondeGroup.class)
public class MessageRequestBean extends BaseMessageRequestBean {
//簽名信息(除該字段外的其他字段按照字典序排序,將值順序拼接在一起,進(jìn)行md5+Base64簽名算法)
@NotBlank(message = "signature為BLANK", groups = ValidateGroup.FirstGroup.class)
private String signature;
...
}
實(shí)現(xiàn)自定義校驗(yàn)邏輯也很簡(jiǎn)單......
1、自定義一個(gè)帶有 @Constraint注解的注解@LogicValidate,validatedBy 屬性指向該注解對(duì)應(yīng)的自定義校驗(yàn)器
@Target({TYPE})
@Retention(RUNTIME)
//指定驗(yàn)證器
@Constraint(validatedBy = LogicValidator.class)
@Documented
public @interface LogicValidate {
String message() default "校驗(yàn)異常";
//分組
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}2、自定義校驗(yàn)器LogicValidator,泛型要關(guān)聯(lián)上自定義的注解和需要校驗(yàn)bean的類型
public class LogicValidator implements ConstraintValidator<LogicValidate, MessageRequestBean> {
@Override
public void initialize(LogicValidate logicValidate) {
}
@Override
public boolean isValid(MessageRequestBean messageRequestBean, ConstraintValidatorContext context) {
String toSignature = StringUtils.join( messageRequestBean.getBizType()
, messageRequestBean.getChannelType()
, messageRequestBean.getData()
, messageRequestBean.getToUser());
String signature = new Base64().encodeAsString(DigestUtils.md5(toSignature));
if (!messageRequestBean.getSignature().equals(signature)) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("signature校驗(yàn)失敗")
.addConstraintViolation();
return false;
}
return true;
}
}可以通過(guò)ConstraintValidatorContext禁用掉默認(rèn)的校驗(yàn)配置,然后自定義校驗(yàn)配置,比如校驗(yàn)失敗后返回的信息。
十、springboot國(guó)際化信息配置
@Configuration
@ConditionalOnMissingBean(value = MessageSource.class, search = SearchStrategy.CURRENT)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Conditional(ResourceBundleCondition.class)
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "spring.messages")
public class MessageSourceAutoConfiguration {
private static final Resource[] NO_RESOURCES = {};
/**
* Comma-separated list of basenames, each following the ResourceBundle convention.
* Essentially a fully-qualified classpath location. If it doesn't contain a package
* qualifier (such as "org.mypackage"), it will be resolved from the classpath root.
*/
private String basename = "messages";
/**
* Message bundles encoding.
*/
private Charset encoding = Charset.forName("UTF-8");
/**
* Loaded resource bundle files cache expiration, in seconds. When set to -1, bundles
* are cached forever.
*/
private int cacheSeconds = -1;
/**
* Set whether to fall back to the system Locale if no files for a specific Locale
* have been found. if this is turned off, the only fallback will be the default file
* (e.g. "messages.properties" for basename "messages").
*/
private boolean fallbackToSystemLocale = true;
/**
* Set whether to always apply the MessageFormat rules, parsing even messages without
* arguments.
*/
private boolean alwaysUseMessageFormat = false;
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
if (StringUtils.hasText(this.basename)) {
messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(this.basename)));
}
if (this.encoding != null) {
messageSource.setDefaultEncoding(this.encoding.name());
}
messageSource.setFallbackToSystemLocale(this.fallbackToSystemLocale);
messageSource.setCacheSeconds(this.cacheSeconds);
messageSource.setAlwaysUseMessageFormat(this.alwaysUseMessageFormat);
return messageSource;
}
public String getBasename() {
return this.basename;
}
public void setBasename(String basename) {
this.basename = basename;
}
public Charset getEncoding() {
return this.encoding;
}
public void setEncoding(Charset encoding) {
this.encoding = encoding;
}
public int getCacheSeconds() {
return this.cacheSeconds;
}
public void setCacheSeconds(int cacheSeconds) {
this.cacheSeconds = cacheSeconds;
}
public boolean isFallbackToSystemLocale() {
return this.fallbackToSystemLocale;
}
public void setFallbackToSystemLocale(boolean fallbackToSystemLocale) {
this.fallbackToSystemLocale = fallbackToSystemLocale;
}
public boolean isAlwaysUseMessageFormat() {
return this.alwaysUseMessageFormat;
}
public void setAlwaysUseMessageFormat(boolean alwaysUseMessageFormat) {
this.alwaysUseMessageFormat = alwaysUseMessageFormat;
}
protected static class ResourceBundleCondition extends SpringBootCondition {
private static ConcurrentReferenceHashMap<String, ConditionOutcome> cache = new ConcurrentReferenceHashMap<String, ConditionOutcome>();
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
String basename = context.getEnvironment()
.getProperty("spring.messages.basename", "messages");
ConditionOutcome outcome = cache.get(basename);
if (outcome == null) {
outcome = getMatchOutcomeForBasename(context, basename);
cache.put(basename, outcome);
}
return outcome;
}
private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context,
String basename) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("ResourceBundle");
for (String name : StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(basename))) {
for (Resource resource : getResources(context.getClassLoader(), name)) {
if (resource.exists()) {
return ConditionOutcome
.match(message.found("bundle").items(resource));
}
}
}
return ConditionOutcome.noMatch(
message.didNotFind("bundle with basename " + basename).atAll());
}
private Resource[] getResources(ClassLoader classLoader, String name) {
try {
return new PathMatchingResourcePatternResolver(classLoader)
.getResources("classpath*:" + name + ".properties");
}
catch (Exception ex) {
return NO_RESOURCES;
}
}
}
}從上面的MessageSource自動(dòng)配置可以看出,可以通過(guò)spring.message.basename指定要配置國(guó)際化文件位置,默認(rèn)值是“message”。spring boot默認(rèn)就支持國(guó)際化的,默認(rèn)會(huì)去resouces目錄下尋找message.properties文件。
這里就不進(jìn)行過(guò)多關(guān)于國(guó)際化相關(guān)信息的介紹了,肯定少不了區(qū)域解析器。springboot國(guó)際化相關(guān)知識(shí)請(qǐng)參考:Spring Boot國(guó)際化(i18n)
到此這篇關(guān)于hibernate-validator如何使用校驗(yàn)框架的文章就介紹到這了,更多相關(guān)hibernate-validator校驗(yàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java使用bcrypt實(shí)現(xiàn)對(duì)密碼加密效果詳解
bcrypt是一種自帶鹽值(自動(dòng)加鹽)的加密方案。本文將通過(guò)示例為大家詳細(xì)介紹這一對(duì)密碼進(jìn)行加密的算法,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-03-03
IDEA社區(qū)版創(chuàng)建spring boot項(xiàng)目的安裝插件的圖文教程
這篇文章主要介紹了IDEA社區(qū)版創(chuàng)建spring boot項(xiàng)目的安裝插件,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11
MyBatis使用<foreach>標(biāo)簽報(bào)錯(cuò)問(wèn)題及解決
這篇文章主要介紹了MyBatis使用<foreach>標(biāo)簽報(bào)錯(cuò)問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
jar包的各種啟動(dòng)方式超詳細(xì)總結(jié)
jar文件是一種軟件包文件格式,通常用于聚合大量的JAVA類文件,以jar為文件擴(kuò)展名,下面這篇文章主要給大家介紹了關(guān)于jar包的各種啟動(dòng)方式,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04
SpringBoot配置線程池的實(shí)現(xiàn)示例
本文主要介紹了SpringBoot配置線程池的實(shí)現(xiàn)示例,主要包括在Spring Boot中創(chuàng)建和配置線程池,包括設(shè)置線程池的大小、隊(duì)列容量、線程名稱等參數(shù),感興趣的可以了解一下2023-09-09
SpringBoot創(chuàng)建Docker鏡像的方法步驟
這篇文章主要介紹了SpringBoot創(chuàng)建Docker鏡像的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
hibernate 中 fetch=FetchType.LAZY 懶加載失敗處理方法
這篇文章主要介紹了hibernate 中 fetch=FetchType.LAZY 懶加載失敗處理方法,需要的朋友可以參考下2017-09-09

