Spring 校驗(yàn)(validator,JSR-303)實(shí)現(xiàn)
什么是JSR-303規(guī)范
JSR 303是Java EE 6中的一項(xiàng)子規(guī)范,叫做Bean Validation,官方參考實(shí)現(xiàn)是hibernate Validator,此實(shí)現(xiàn)與Hibernate ORM沒有任何關(guān)系。JSR 303用于對(duì)Java Bean中的字段的值進(jìn)行驗(yàn)證。
與Spring MVC結(jié)合
Spring-mvc.xml配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<!--JSR-303--> < mvc:annotation-driven validator = "validator" /> < bean id = "validator" class = "org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" > < property name = "providerClass" value = "org.hibernate.validator.HibernateValidator" /> < property name = "validationMessageSource" ref = "messageSource" /> </ bean > < bean id = "messageSource" class = "org.springframework.context.support.ReloadableResourceBundleMessageSource" > < property name = "basename" value = "validatemessage" /> < property name = "useCodeAsDefaultMessage" value = "false" /> < property name = "defaultEncoding" value = "UTF-8" /> < property name = "cacheSeconds" value = "60" /> </ bean > < bean id = "webBindingInitializer" class = "org.springframework.web.bind.support.ConfigurableWebBindingInitializer" > < property name = "conversionService" > < bean class = "org.springframework.format.support.FormattingConversionServiceFactoryBean" ></ bean > </ property > < property name = "validator" ref = "validator" /> </ bean > |
實(shí)體類添加驗(yàn)證注解
這里貼出部分代碼,知道如何加注解即可:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
import com.lemontree.common.utils.AjaxResult; import com.lemontree.common.utils.StringUtil; import com.lemontree.common.utils.email.EmailUtils; import org.hibernate.validator.constraints.NotEmpty; import java.util.Date; public class User { /** * This field was generated by MyBatis Generator. * This field corresponds to the database column user.id * * @mbg.generated Thu Mar 16 13:27:38 CST 2017 */ private Integer id; /** * This field was generated by MyBatis Generator. * This field corresponds to the database column user.user_name * * @mbg.generated Thu Mar 16 13:27:38 CST 2017 */ @NotEmpty (message = "用戶名不能為空" ) private String userName; /** * This field was generated by MyBatis Generator. * This field corresponds to the database column user.password * * @mbg.generated Thu Mar 16 13:27:38 CST 2017 */ @NotEmpty (message = "密碼不能為空" ) private String password; } |
控制器驗(yàn)證注解添加
將@Validated 注解跟在實(shí)體類前面,BindingResult緊跟其后:
1
2
3
4
5
6
7
8
9
10
11
12
|
@RequestMapping (value = "/login.htm" , method = RequestMethod.POST) public @ResponseBody AjaxResult login( @Validated User user, BindingResult bindingResult, HttpServletRequest request, HttpServletResponse response) { if (bindingResult.hasErrors()){ List<FieldError> errorses = bindingResult.getFieldErrors(); if (CollectionUtils.isNotEmpty(errorses)){ errorses.forEach(item->{ System.out.println(item.getDefaultMessage()); }); } } } |
Java Hibernate Validator JSR-303驗(yàn)證
JSR-303是JAVA EE 6中的一項(xiàng)子規(guī)范,叫做 Bean Validation,Hibernate Validator是Bean Validation 的參考實(shí)現(xiàn)。
實(shí)際使用就是通過注解來給字段添加約束,然后校驗(yàn)字段是否符合規(guī)范,如果不符合就會(huì)拋出異常,以此來減少校驗(yàn)數(shù)據(jù)的代碼,并保證拿到的數(shù)據(jù)都是符合規(guī)范的,也可以和Spring框架配合使用
集成
官方文檔
https://mvnrepository.com/artifact/org.hibernate/hibernate-validator
https://mvnrepository.com/artifact/javax.validation/validation-api
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
< dependency > < groupId >org.hibernate</ groupId > < artifactId >hibernate-validator</ artifactId > < version >6.0.10.Final</ version > </ dependency > < dependency > < groupId >org.glassfish</ groupId > < artifactId >javax.el</ artifactId > < version >3.0.1-b09</ version > </ dependency > < dependency > < groupId >javax.validation</ groupId > < artifactId >validation-api</ artifactId > < version >2.0.1.Final</ version > </ dependency > |
使用
校驗(yàn)對(duì)象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public class JsrTest { @NotNull (message = "id不能為空!" ) @Min (value = 1 , message = "Id只能大于等于1" ) Integer id; @NotNull (message = "姓名不能為空!" ) String name; public void validateParams() { Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); //獲取一個(gè)驗(yàn)證器 Set<ConstraintViolation<JsrTest>> violationSet = validator.validate( this ); //驗(yàn)證數(shù)據(jù),獲取到錯(cuò)誤集合 Iterator<ConstraintViolation<JsrTest>> iterator = violationSet.iterator(); if (iterator.hasNext()) { String errorMessage = iterator.next().getMessage(); //獲取到錯(cuò)誤信息 throw new ValidationException(errorMessage); } } public static void main(String args[]) { JsrTest req = new JsrTest(); req.id = 1 ; req.validateParams(); } } |
像上面那樣將在屬性上添加注解即可聲明約束
校驗(yàn)屬性
上面是校驗(yàn)整個(gè)對(duì)象,也可以單獨(dú)校驗(yàn)?zāi)硞€(gè)字段:
1
|
validator.validateProperty(object, "name" ); |
分組校驗(yàn)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public class JsrTest { @NotNull (message = "id不能為空!" , groups = {ValidationGroup. class }) @Min (value = 1 , message = "Id只能大于等于1" ) Integer id; @NotNull (message = "姓名不能為空!" , groups = {ValidationGroup. class }) String name; @DecimalMin (value = "1.1" ) double price; int date; public static void validateParams(JsrTest jsrTest) { Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); Set<ConstraintViolation<JsrTest>> violationSet = validator.validate(jsrTest, ValidationGroup. class ); Iterator<ConstraintViolation<JsrTest>> iterator = violationSet.iterator(); if (iterator.hasNext()) { String errorMessage = iterator.next().getMessage(); throw new ValidationException(errorMessage); } } public static void main(String args[]) { JsrTest req = new JsrTest(); validateParams(req); } public interface ValidationGroup { } } |
分組校驗(yàn)所指定的calss必須是一個(gè)接口,可以指定多個(gè)
自定義約束
通常情況下,框架提供的注解已經(jīng)可以滿足正常的驗(yàn)證需求,但是我們也可以自定義注解來滿足我們的需求
我們這里的例子是所注釋的字符串中不能包含指定字符
1
2
3
4
5
6
7
8
9
10
|
@Target (FIELD) //元注解,定義該注解使用在字段上 @Retention (RUNTIME) //定義注解的生命周期 @Constraint (validatedBy = CustomValidator. class ) //指明該注解的校驗(yàn)器 @Documented //表示該注解會(huì)被添加到JavaDoc中 public @interface CustomConstraints { String message() default "默認(rèn)異常message" ; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; //這個(gè)屬性可以用來標(biāo)注錯(cuò)誤的嚴(yán)重等級(jí),但是并不被API自身所使用 String value() default " " ; } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; /** * 需要實(shí)現(xiàn)ConstraintValidator接口 * 泛型的第一個(gè)參數(shù)是自定義的注解,第二個(gè)參數(shù)注解所注釋的字段的類型 */ public class CustomValidator implements ConstraintValidator<CustomConstraints, String> { private String value; /** * 初始化調(diào)用,拿到注解所指定的value * * @param constraintAnnotation */ @Override public void initialize(CustomConstraints constraintAnnotation) { value = constraintAnnotation.value(); } /** * @param value 注釋的字段的值 * @param context * @return true 通過驗(yàn)證,false 未通過驗(yàn)證 */ @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (value != null && value.contains( this .value)) { context.disableDefaultConstraintViolation(); //禁用默認(rèn)的消息 context.buildConstraintViolationWithTemplate( "新添加的錯(cuò)誤消息" ).addConstraintViolation(); return false ; } return true ; } } |
然后就可以和其他注解一樣使用它了
封裝
或者是將驗(yàn)證參數(shù)的代碼提取去出來,單獨(dú)寫一個(gè)方法
1
2
3
4
5
6
7
8
9
|
public static void validateParams(Object object) { Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); //獲取一個(gè)驗(yàn)證器 Set<ConstraintViolation<Object>> violationSet = validator.validate(object); //驗(yàn)證數(shù)據(jù),獲取到錯(cuò)誤集合 Iterator<ConstraintViolation<Object>> iterator = violationSet.iterator(); if (iterator.hasNext()) { String errorMessage = iterator.next().getMessage(); //獲取到錯(cuò)誤信息 throw new ValidationException(errorMessage); } } |
當(dāng)然這里也可以不拋出異常,而返回一個(gè)boolean值,如何封裝看實(shí)際需求
配合Spring使用
1
2
3
4
5
6
7
8
|
@GetMapping ( "/test" ) public Integer lookCanBuyGoods( @Valid JsrTest req, BindingResult result) throws Exception { if (result.hasErrors()) { throw new ValidationException(result.getAllErrors().get( 0 ).getDefaultMessage()); } //do something... return 1 ; } |
@Valid添加這個(gè)注解之后就會(huì)對(duì)參數(shù)進(jìn)行驗(yàn)證,如果在其后沒有跟BindingResult,驗(yàn)證不通過就會(huì)直接拋出異常,如果添加了BindingResult參數(shù),就不會(huì)直接拋出異常,而會(huì)把異常信息存儲(chǔ)在BindingResult中,供開發(fā)者自行處理
如果想要使用分組可以這樣
1
2
3
4
5
6
7
8
|
@GetMapping ( "/test" ) public Integer test( @Validated (JsrTest.ValidationGroup. class ) JsrTest req, BindingResult result) throws Exception { if (result.hasErrors()) { throw new ValidationException(result.getAllErrors().get( 0 ).getDefaultMessage()); } //do something... return 1 ; } |
@Validated如果不使用分組其作用和@Valid一致
注解使用說明
Constraint | 詳細(xì)信息 |
---|---|
@Null | 被注釋的元素必須為 null |
@NotNull | 被注釋的元素必須不為 null |
@AssertTrue | 被注釋的元素必須為 true |
@AssertFalse | 被注釋的元素必須為 false |
@Min(value) | 被注釋的元素必須是一個(gè)數(shù)字,其值必須大于等于指定的最小值 |
@Max(value) | 被注釋的元素必須是一個(gè)數(shù)字,其值必須小于等于指定的最大值 |
@DecimalMin(value) | 被注釋的元素必須是一個(gè)數(shù)字,其值必須大于等于指定的最小值 |
@DecimalMax(value) | 被注釋的元素必須是一個(gè)數(shù)字,其值必須小于等于指定的最大值 |
@Size(max, min) | 被注釋的元素的大小必須在指定的范圍內(nèi) |
@Digits (integer, fraction) | 被注釋的元素必須是一個(gè)數(shù)字,其值必須在可接受的范圍內(nèi) |
@Past | 被注釋的元素必須是一個(gè)過去的日期 |
@PastOrPresent | 被注釋的元素必須是過去或現(xiàn)在的日期 |
@Future | 被注釋的元素必須是一個(gè)將來的日期 |
@FutureOrPresent | 被注釋的元素必須是將來或現(xiàn)在的日期 |
@Pattern(value) | 被注釋的元素必須符合指定的正則表達(dá)式 |
@Digits(integer =, fraction =) | 驗(yàn)證字符串是否是符合指定格式的數(shù)字,interger指定整數(shù)精度,fraction指定小數(shù)精度 |
驗(yàn)證是否是郵件地址,如果為null,不進(jìn)行驗(yàn)證,算通過驗(yàn)證 | |
@NotBlank | 字符串不能是Null還有被Trim的長(zhǎng)度要大于0 |
@NotEmpty | 不能為null,且長(zhǎng)度大于0 |
@Negative | 被注釋的元素必須是負(fù)數(shù) |
@NegativeOrZero | 被注釋的元素必須是負(fù)數(shù)或0 |
@Positive | 必須是正數(shù) |
@PositiveOrZero | 必須是正數(shù)或0 |
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://blog.csdn.net/u012188107/article/details/72770854