@Valid与@Validated区别
1.@Valid与@Validated作用
@Valid与@Validated都是用来校验接收参数的。@Valid是使用Hibernate validation的时候使用@Validated是只用Spring Validator校验机制使用说明:java的JSR303声明了@Valid这类接口,而Hibernate-validator对其进行了实现。@Validated与@Valid区别:@Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上,不支持嵌套检测
@Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上,支持嵌套检测注意:SpringBoot使用@Valid注解需要引入如下POM<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2.为什么要用@Valid或@Validated
@Valid与@Validated都是用来校验接收参数的,如果不使用注解校验参数,那么就需要在业务代码中校验,这
样会增加很多的工作量并且代码不优美。
@RestController
@RequestMapping("/valid")
public class ValidControllerTest {public static final Logger logger = LoggerFactory.getLogger(ValidControllerTest.class.getName());@PostMapping("/login")public String doLogin(@RequestBody LoginUser loginUser){if(StringUtils.isEmpty(loginUser.getPhone())){//抛出异常logger.error("伪代码抛出异常---可以用全局异常捕获");}if(StringUtils.isEmpty(loginUser.getPassWord())){//抛出异常logger.error("伪代码抛出异常---可以用全局异常捕获");}return "数据校验通过";}
}如果需要判断多个参数,那就需要写多个if判断,这样会增加编码工作量并且这样写的不优美。
3.使用@Valid或@Validated来做一个简单的案例
如果@Valid|Validated校验不通过,那么错误信息就会封装到BindingResult对象了,可以通过
BindingResult的相关方法获取详细的错误信息并返回给用户。 代码如下:public class LoginUser {@NotBlank(message = "手机号码不能为空")private String phone;@NotBlank(message = "密码不能为空")private String passWord;
}@RestController
@RequestMapping("/valid")
public class ValidControllerTest {public static final Logger logger = LoggerFactory.getLogger(ValidControllerTest.class.getName());@PostMapping("/login")public String doLogin(@RequestBody @Validated LoginUser loginUser, BindingResult bindingResult){if(bindingResult.hasErrors()){List<ObjectError> allErrors = bindingResult.getAllErrors();allErrors.forEach( v ->{logger.error(v.getObjectName()+"======"+v.getField()+"======"+v.getDefaultMessage());});}return "数据校验通过";}
}loginUser======phone======密码不能为空
loginUser======passWord======手机号码不能为空
4.使用@Valid实现嵌套检测
什么是嵌套检测?嵌套检测就是在一个beanA中,存在另外一个beanB属性。嵌套检测beanA同时也检测beanB。public class AgeBean {@NotNull(message = "年龄不能为空")@Max(value = 120,message = "年龄不能超过120岁")@Min(value = 0,message = "年龄不能为负数")private int age;
}public class LoginUser {@NotBlank(message = "手机号码不能为空")private String phone;@NotBlank(message = "密码不能为空")private String passWord;@NotNull(message = "ageBean不能为null")private AgeBean ageBean;
}注意:LoginUser中ageBean属性没有添加@Valid注解,所以只能检测ageBean属性不为空,但是不能
检测AgeBean对象中age属性定义的最大值和最小值。(@Validated不能添加属性上面,不支持嵌套检测所以
只有使用@Valid)
3. 嵌套验证@Validated:用在方法入参上无法单独提供嵌套验证功能。不能用在成员属性(字段)上,也无法提示框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。@Valid:用在方法入参上无法单独提供嵌套验证功能。能够用在成员属性(字段)上,提示验证框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。也就是说单独一个@Validated或者@Valid都无法提供嵌套验证功能,都需要再结合一个@Valid注解进行嵌套验证@RequestMapping("/valid")
public class ValidControllerTest {public static final Logger logger = LoggerFactory.getLogger(ValidControllerTest.class.getName());@PostMapping("/login")public String doLogin(@RequestBody @Validated LoginUser loginUser, BindingResult bindingResult){if(bindingResult.hasErrors()){List<ObjectError> allErrors = bindingResult.getAllErrors();allErrors.forEach( v ->{logger.error(v.getObjectName()+"======"+v.getField()+"======"+v.getDefaultMessage());});}return "数据校验通过";}
}模拟入参:{"phone":"111","passWord":"","ageBean":{"age":-1}
}只抛出密码不为空,没有嵌套检测ageBean属性中的age属性最大值最小值。
loginUser======passWord======密码不能为空添加上@Valid注解,重新测试
public class LoginUser {@NotBlank(message = "手机号码不能为空")private String phone;@NotBlank(message = "密码不能为空")private String passWord;@Valid@NotNull(message = "ageBean不能为null")private AgeBean ageBean;
}模拟参数:{"phone":"111","passWord":"","ageBean":{"age":-1}
}添加@Valid注解便可以嵌套检测ageBean属性中的age属性的最大值|最小值loginUser======年龄不能为负数
loginUser======密码不能为空
5.常用注解说明
@AssertFalse:所注解的元素必须是Boolean类型,并且值为false@AssertTrue:所注解的元素必须是Boolean类型,并且值为true@DecimalMax:所注解的元素必须是数字,并且值要小于或等于给定的BigDecimalString值@DecimalMin:所注解的元素必须是数字,并且值要大于或等于给定的BigDecimalString值@Digits:所注解的元素必须是数字,并且它的值必须有指定的位数@Email:所注解的元素要匹配指定的正则表达式@Max:所注解的元素必须是数字,并且值要小于或等于给定的值。注意如果@Max所注解的元素是null,则@Max注解
会返回true,所以应该把@Max注解和@NotNull注解结合使用@Min:所注解的元素必须是数字,并且值要大于或等于给定的值。注意如果@Min所注解的元素是null,则@Min注解
会返回true,即也会通过校验,所以应该把@Min注解和@NotNull注解结合使用。@NotBlank:所注解的元素不能为null且不能为空白,并且必须至少包含一个非空白字符,用于校验CharSequence(含String、StringBuilder和StringBuffer)。只支持字符类型。@NotEmpty:所注解的元素不能为null且长度大于0,可以是空白,用于校验CharSequence、数组、Collection和Map@NotNull:所注解的元素不能为null,接受任何类型@Null:所注解的元素必须为null,接受任何类型@Pattern:所注解的元素必须匹配指定的正则表达式。注意如果@Pattern所注解的元素是null,则@Pattern注解会返回
true,即也会通过校验,所以应该把@Pattern注解和@NotNull注解结合使用@Size:所注解的元素必须符合指定的大小,该注解可用于数组,CharSequence(含String、StringBuilder和
StringBuffer),Collection和Map。注意如果@Size所注解的元素是null,则@Size注解会返回true,即也
会通过校验,所以应该把@Size注解和@NotNull注解结合使用
Validated、Valid、Validator,他们的区别你知道几个
1. 结论先出
Valid VS Validated 相同点
- 都可以对方法和参数进行校验
- @Valid和@Validated
两种注释都会导致应用标准Bean验证。
如果验证不通过会抛出
BindException
异常,并变成400(BAD_REQUEST)响应;或者可以通过Errors
或BindingResult
参数在控制器内本地处理验证错误。另外,如果参数前有@RequestBody
注解,验证错误会抛出MethodArgumentNotValidException
异常。
JSR 380
JSR 380 是用于 bean 验证的 Java API 规范,是 Jakarta EE 和 JavaSE 的一部分。这确保 bean 的属性满足特定条件,使用诸如@NotNull、@Min和@Max 之类的注释。
此版本需要 Java 8 或更高版本,并利用 Java 8 中添加的新功能,例如类型注释和对Optional和LocalDate等新类型的支持。
有关规范的完整信息,请继续阅读JSR 380。
Valid VS Validated 不同点?
javax.validation.Valid
- 是JSR-303规范标准注解支持,是一个标记注解。
- 注解支持
ElementType#METHOD
,ElementType#FIELD
,ElementType#CONSTRUCTOR
,ElementType#PARAMETER
,ElementType#TYPE_USE
org.springframework.validation.annotation.Validated
- 是Spring 做得一个自定义注解,增强了分组功能。
- 注解支持
ElementType#TYPE
,ElementType#METHOD
,ElementType#PARAMETER
@Valid
和@Validated
区别
Validator
Bean Validation 2.0(JSR 380)定义了用于实体和方法验证的元数据模型和API,Hibernate Validator是目前最好的实现
Validator
接口有三个方法,可用于验证整个实体或仅验证实体的单个属性
Validator#validate()
验证所有bean的所有约束Validator#validateProperty()
验证单个属性Validator#validateValue()
检查给定类的单个属性是否可以成功验证不管是
requestBody参数校验
还是方法级别的校验
,最终都是调用Hibernate Validator
执行校验,Spring Validation
只是做了一层封装。
验证用户的输入是我们大多数应用程序中的常见功能。在 Java 生态系统中,我们专门使用Java Standard Bean Validation API来支持这一点。此外,从 4.0 版本开始,这也与 Spring 很好地集成在一起.
在接下来的部分中,让我们详细了解它们。
2. @Valid和@Validated 注解
在 Spring 中,我们使用 JSR-303 的@Valid注释进行方法级别验证。此外,我们还使用它来标记成员属性以进行验证。但是,此注释不支持组验证。
组有助于限制验证期间应用的约束。一个特殊的用例是 UI 界面(UI wizards)。在这里,在第一步中,我们可能有某个字段子组。在后续步骤中,可能有另一个组属于同一个 bean。因此我们需要在每一步中对这些有限的字段应用约束,但@Valid不支持这一点。
在这种情况下,对于组级别,我们必须使用 Spring 的@Validated,它是 JSR-303 的@Valid的变体。这是在方法级别使用的。对于标记成员属性,我们继续使用@Valid注释。
现在,让我们直接进入并通过一个例子来看看这些注解的用法。
3. 例子
让我们考虑一个使用 Spring Boot 开发的简单用户注册。首先,我们将只有名称和密码属性:
public class UserAccount {@NotNull@Size(min = 4, max = 15)private String password;@NotBlankprivate String name;
// standard constructors / setters / getters / toString
}
接下来,让我们看看控制器。在这里,我们将使用带有@Valid注释的saveBasicInfo方法来验证用户输入:
@RequestMapping(value = "/saveBasicInfo", method = RequestMethod.POST)
public String saveBasicInfo(@Valid @ModelAttribute("useraccount") UserAccount useraccount, BindingResult result, ModelMap model) {if (result.hasErrors()) {return "error";}return "success";
}
现在让我们测试这个方法:
@Test
public void givenSaveBasicInfo_whenCorrectInput_thenSuccess() throws Exception {this.mockMvc.perform(MockMvcRequestBuilders.post("/saveBasicInfo").accept(MediaType.TEXT_HTML).param("name", "test123").param("password", "pass")).andExpect(view().name("success")).andExpect(status().isOk()).andDo(print());
}
确认测试运行成功后,我们现在扩展功能。下一个合乎逻辑的步骤是将其转换为复杂用户注册。第一步,名称和密码保持不变。在第二步中,我们将获取诸如年龄 和 电话之类的附加信息。因此,我们将使用这些附加字段更新我们的域对象:
public class UserAccount {@NotNull@Size(min = 4, max = 15)private String password;@NotBlankprivate String name;@Min(value = 18, message = "Age should not be less than 18")private int age;@NotBlankprivate String phone;
// standard constructors / setters / getters / toString
}
但是,这一次我们会注意到之前的测试失败了。这是因为我们没有传入age和phone字段。为了支持这种行为,我们需要组验证和@Validated注释。
为此,我们需要对字段进行分组,创建两个不同的组。首先,我们需要创建两个标记接口。每个组或每个步骤单独一个。我们可以参考我们关于组验证的文章以了解具体的实现方式。在这里,让我们关注注释的差异。
我们将有第一步的BasicInfo接口和第二步的 AdvanceInfo 。此外,我们将更新UserAccount类以使用这些标记接口,如下所示:
public class UserAccount {@NotNull(groups = BasicInfo.class)@Size(min = 4, max = 15, groups = BasicInfo.class)private String password;@NotBlank(groups = BasicInfo.class)private String name;@Min(value = 18, message = "Age should not be less than 18", groups = AdvanceInfo.class)private int age;@NotBlank(groups = AdvanceInfo.class)private String phone;
// standard constructors / setters / getters / toString
}
此外,我们现在将更新我们的控制器以使用@Validated批注而不是@Valid:
@RequestMapping(value = "/saveBasicInfoStep1", method = RequestMethod.POST)
public String saveBasicInfoStep1(@Validated(BasicInfo.class) @ModelAttribute("useraccount") UserAccount useraccount, BindingResult result, ModelMap model) {if (result.hasErrors()) {return "error";}return "success";
}
由于此更新,我们的测试现在成功运行。现在让我们也测试一下这个新方法:
@Test
public void givenSaveBasicInfoStep1_whenCorrectInput_thenSuccess() throws Exception {this.mockMvc.perform(MockMvcRequestBuilders.post("/saveBasicInfoStep1").accept(MediaType.TEXT_HTML).param("name", "test123").param("password", "pass")).andExpect(view().name("success")).andExpect(status().isOk()).andDo(print());
}
这也运行成功。因此,我们可以看到@Validated的使用 对于组验证至关重要。
接下来,让我们看看@Valid如何触发嵌套属性的验证。
4.使用@Valid嵌套校验
@Valid注释用于校验嵌套属性。这会触发嵌套对象的验证。例如,在我们当前的场景中,让我们创建一个 UserAddress 对象:
public class UserAddress {@NotBlankprivate String countryCode;
// standard constructors / setters / getters / toString
}
为了确保此嵌套对象的验证,我们将使用@Valid注释来装饰该属性:
public class UserAccount { //... @Valid @NotNull(groups = AdvanceInfo.class) private UserAddress useraddress;
// standard constructors / setters / getters / toString
}
5. 组合使用@Valid和@Validated 进行集合校验
如果请求体直接传递了json
数组给后台,并希望对数组中的每一项都进行参数校验。此时,如果我们直接使用java.util.Collection
下的list
或者set
来接收数据,参数校验并不会生效!我们可以使用自定义list
集合来接收参数:
- 包装
List
类型,并声明@Valid
注解
import javax.validation.Valid;
import java.util.*;/*** @Title: 参数校验工具类, 用于校验List<E> 类型的请求参数 * * @ClassName: com.devicemag.core.BO.ValidList.java * * @Description: * * * @Copyright 2020-2021 - Powered By 研发中心 * * @author: 王延飞 * * @date: 2020/12/25 20:23 * * @version V1.0*/
public class ValidList<E> implements List<E> {@Validprivate List<E> list = new ArrayList<>();@Overridepublic int size() {return list.size();}@Overridepublic boolean isEmpty() {return list.isEmpty();}@Overridepublic boolean contains(Object o) {return list.contains(o);}@Overridepublic Iterator<E> iterator() {return list.iterator();}@Overridepublic Object[] toArray() {return list.toArray();}@Overridepublic <T> T[] toArray(T[] a) {return list.toArray(a);}@Overridepublic boolean add(E e) {return list.add(e);}@Overridepublic boolean remove(Object o) {return list.remove(o);}@Overridepublic boolean containsAll(Collection<?> c) {return list.containsAll(c);}@Overridepublic boolean addAll(Collection<? extends E> c) {return list.addAll(c);}@Overridepublic boolean addAll(int index, Collection<? extends E> c) {return list.addAll(index, c);}@Overridepublic boolean removeAll(Collection<?> c) {return list.removeAll(c);}@Overridepublic boolean retainAll(Collection<?> c) {return list.retainAll(c);}@Overridepublic void clear() {list.clear();}@Overridepublic E get(int index) {return list.get(index);}@Overridepublic E set(int index, E element) {return list.set(index, element);}@Overridepublic void add(int index, E element) {list.add(index, element);}@Overridepublic E remove(int index) {return list.remove(index);}@Overridepublic int indexOf(Object o) {return list.indexOf(o);}@Overridepublic int lastIndexOf(Object o) {return list.lastIndexOf(o);}@Overridepublic ListIterator<E> listIterator() {return list.listIterator();}@Overridepublic ListIterator<E> listIterator(int index) {return list.listIterator(index);}@Overridepublic List<E> subList(int fromIndex, int toIndex) {return list.subList(fromIndex, toIndex);}public List<E> getList() {return list;}public void setList(List<E> list) {this.list = list;} // 一定要记得重写toString方法 @Overridepublic String toString() {return "ValidList{" + "list=" + list + '}';}
}
比如,我们需要一次性保存多个UserAccount
对象,Controller
层的方法可以这么写:
@PostMapping("/saveList")
public Result saveList(@RequestBody @Validated(UserAccount.class) ValidationList<UserAccount> userList) {// 校验通过,才会执行业务逻辑处理return Result.ok();
}
6. 自定义校验
validator-api-2.0的约束注解有22个,具体我们看下面表格
空与非空检查
注解 | 支持Java类型 | 说明 |
---|---|---|
@Null | Object | 为null |
@NotNull | Object | 不为null |
@NotBlank | CharSequence | 不为null,且必须有一个非空格字符 |
@NotEmpty | CharSequence、Collection、Map、Array | 不为null,且不为空(length/size>0) |
Boolean值检查
注解 | 支持Java类型 | 说明 | 备注 |
---|---|---|---|
@AssertTrue | boolean、Boolean | 为true | 为null有效 |
@AssertFalse | boolean、Boolean | 为false | 为null有效 |
日期检查
注解 | 支持Java类型 | 说明 | 备注 |
---|---|---|---|
@Future | Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate | 验证日期为当前时间之后 | 为null有效 |
@FutureOrPresent | Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate | 验证日期为当前时间或之后 | 为null有效 |
@Past | Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate | 验证日期为当前时间之前 | 为null有效 |
@PastOrPresent | Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate | 验证日期为当前时间或之前 | 为null有效 |
数值检查
注解 | 支持Java类型 | 说明 | 备注 |
---|---|---|---|
@Max | BigDecimal、BigInteger,byte、short、int、long以及包装类 | 小于或等于 | 为null有效 |
@Min | BigDecimal、BigInteger,byte、short、int、long以及包装类 | 大于或等于 | 为null有效 |
@DecimalMax | BigDecimal、BigInteger、CharSequence,byte、short、int、long以及包装类 | 小于或等于 | 为null有效 |
@DecimalMin | BigDecimal、BigInteger、CharSequence,byte、short、int、long以及包装类 | 大于或等于 | 为null有效 |
@Negative | BigDecimal、BigInteger,byte、short、int、long、float、double以及包装类 | 负数 | 为null有效,0无效 |
@NegativeOrZero | BigDecimal、BigInteger,byte、short、int、long、float、double以及包装类 | 负数或零 | 为null有效 |
@Positive | BigDecimal、BigInteger,byte、short、int、long、float、double以及包装类 | 正数 | 为null有效,0无效 |
@PositiveOrZero | BigDecimal、BigInteger,byte、short、int、long、float、double以及包装类 | 正数或零 | 为null有效 |
@Digits(integer = 3, fraction = 2) | BigDecimal、BigInteger、CharSequence,byte、short、int、long以及包装类 | 整数位数和小数位数上限 | 为null有效 |
其他
注解 | 支持Java类型 | 说明 | 备注 |
---|---|---|---|
@Pattern | CharSequence | 匹配指定的正则表达式 | 为null有效 |
CharSequence | 邮箱地址 |
为null有效,默认正则 '.*'
|
|
@Size | CharSequence、Collection、Map、Array | 大小范围(length/size>0) | 为null有效 |
hibernate-validator扩展约束(部分)
注解 | 支持Java类型 | 说明 |
---|---|---|
@Length | String | 字符串长度范围 |
@Range | 数值类型和String | 指定范围 |
@URL | URL地址验证 |
自定义约束注解
除了以上提供的约束注解(大部分情况都是能够满足的),我们还可以根据自己的需求自定义自己的约束注解
定义自定义约束,有三个步骤
- 创建约束注解
- 实现一个验证器
- 定义默认的错误信息
那么下面就直接来定义一个简单的验证手机号码的注解
@Documented
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Constraint(validatedBy = {MobileValidator.class})
@Retention(RUNTIME)
@Repeatable(Mobile.List.class)
public @interface Mobile {/*** 错误提示信息,可以写死,也可以填写国际化的key*/String message() default "手机号码不正确";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};String regexp() default "^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$";@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})@Retention(RUNTIME)@Documented@interface List {Mobile[] value();}
}
关于注解的配置这里不说了,自定义约束需要下面3个属性
message
错误提示信息,可以写死,也可以填写国际化的keygroups
分组信息,允许指定此约束所属的验证组(下面会说到分组约束)payload
有效负载,可以通过payload来标记一些需要特殊处理的操作
@Repeatable
注解和List
定义可以让该注解在同一个位置重复多次,通常是不同的配置(比如不同的分组和消息)@Constraint(validatedBy = {MobileValidator.class})
该注解是指明我们的自定义约束的验证器,那下面就看一下验证器的写法,需要实现javax.validation.ConstraintValidator
接口
public class MobileValidator implements ConstraintValidator<Mobile, String> {/*** 手机验证规则*/private Pattern pattern;@Overridepublic void initialize(Mobile mobile) {pattern = Pattern.compile(mobile.regexp());}@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {if (value == null) {return true;}return pattern.matcher(value).matches();}
}
ConstraintValidator
接口定义了在实现中设置的两个类型参数。
- 第一个指定要验证的注解类(如
Mobile
), - 第二个指定验证器可以处理的元素类型(如
String
);initialize()
方法可以访问约束注解的属性值;isValid()
方法用于验证,返回true表示验证通过
Bean验证规范建议将空值视为有效。如果
null
不是元素的有效值,则应使用@NotNull
显式注释
到这里我们自定义的约束就写好了,可以用个例子来测试一下
public class MobileTest {public void setMobile(@Mobile String mobile) {// to do }private static ExecutableValidator executableValidator;@BeforeAllpublic static void setUpValidator() {ValidatorFactory factory = Validation.buildDefaultValidatorFactory();executableValidator = factory.getValidator().forExecutables();}@Testpublic void manufacturerIsNull() throws NoSuchMethodException {MobileTest mobileTest = new MobileTest();Method method = MobileTest.class.getMethod("setMobile", String.class);Object[] parameterValues = {"1111111"};Set<ConstraintViolation<MobileTest>> violations = executableValidator.validateParameters(mobileTest, method, parameterValues);violations.forEach(violation -> System.out.println(violation.getMessage()));}
}
手机号码不正确
工作原理
@Validated
的工作原理
方法级别参数校验
在每个参数前面声明约束注解,然后通过解析参数注解完成校验,这就是方法级别的参数校验。 这种方式可以用于任何的Spring Bean
的方法上,一般来说,这种方式一般会采用AOP
的Around
增强完成 在Spring中,是通过以下步骤完成
MethodValidationPostProcessor
在Bean的初始化完成之后,判断是否要进行AOP代理(类是否被@Validated
标记)MethodValidationInterceptor
拦截所有方法,执行校验逻辑- 委派
Validator
执行参数校验和返回值校验,得到ConstraintViolation
- 处理
ConstraintViolation
结论
总之,对于任何基本验证,我们将在方法调用中使用 JSR @Valid注释。另一方面,对于任何组验证,包括组序列,我们需要 在我们的方法调用中使用 Spring 的@Validated注释。所述@Valid 还需要注释来触发嵌套属性的验证。
@Validated
的原理本质还是AOP
。在方法校验上,利用AOP动态拦截方法,利用JSR303 Validator
实现完成校验。在Bean的属性校验上,则是基于Bean的生命周期,在其初始化前后完成校验Spring Validator
本质实现还是JSR303 Validaotr
,只是能让其更好的适配Spring Context
@javax.validation.Valid
是JSR303
的核心标记注解,但是在Spring Framework
中被@Validated
取代,但是Spring Validator
的实现可以支持兼容@javax.validation.Valid
例如,在MethodValidationPostProcessor
提供了setValidatedAnnotationType
方法,替换默认的@Validated
在Spring MVC
中,RequestResponseBodyMethodProcessor
对@RequestBody
和@ResponseBody
的校验处理,就兼容了@javax.validation.Valid
和@Validated
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {@Overrideprotected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {Annotation[] annotations = parameter.getParameterAnnotations();for (Annotation ann : annotations) {Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[]{hints});binder.validate(validationHints);break;}}}
}
参考链接:
https://blog.csdn.net/fly910905/article/details/119850168
@Valid与@Validated区别相关推荐
- 校验注解:@Valid 和 @Validated区别与用法(附详细案例)
一.案例对象 本文章会以案例为主,讲解@Valid 和 @Validated这两个注解的区别与用法. 1.首先,创建一个学生对象,如下: import lombok.Data;/*** 学生对象*/ ...
- @Validated注解详解,分组校验,嵌套校验,@Valid和@Validated 区别,Spring Boot @Validated
技术栈: spring boot 2.3.3.RELEASE hibernate-validator 文末附项目源代码 目录 简述 项目依赖 全局异常处理类 基础参数校验 实体类 控制类 测试 嵌套参 ...
- 常用校验注解@NotEmpty,@NotBlank,@NotNull,@Valid,@Validated用法区别,以及搭配 BindingResult使用,嵌套验证等《使用|CSDN创作打卡》
文章目录 一.常用注解@NotEmpty,@NotBlank,@NotNull介绍 二.@Valid与@Validated的使用与区别 2.1@Valid与@Validated区别: 2.2嵌套验证 ...
- @Valid和@Validated
简介 @Validation是一套帮助我们继续对传输的参数进行数据校验的注解,通过配置Validation可以很轻松的完成对数据的约束,配合BindingResult可以直接提供参数验证结果 所有参数 ...
- 一文探知@Valid和@Validated 的区别
使用spring boot开发的道友们,或许或多或少有用到过这两个注解,那么今天我们就来探查一下这两者的异同. 两者都可以使开发人员写的注解校验规则生效,例如在某一个字段上加上@NotNull.@No ...
- 《SpringBoot篇》12.@Valid与@Validated的区别
陈老老老板 说明:工作了,学习一些新的技术栈,边学习边总结,各位一起加油.需要注意的地方都标红了,还有资源的分享. 一起加油. 1.介绍 说明: 其实@Valid 与 @Validated都是做数据校 ...
- Java校验框架使用@Valid、@Validated、OVAL+Groovy
@Valid 与 @Validated Spring Validation验证框架对参数的验证机制提供了@Validated; javax提供了@Valid,配合BindingResult可以直接提供 ...
- valid 和 validated的使用小结
[技术分享]Bean Validation使用篇 2020-07-09马平凡(鲁吉英) Bean Validation 使用篇 在开始文章之前,先讲一下为什么会写这篇文章,以及阅读后的相应收益. Be ...
- 【转】tf中的padding方式SAME和VALID有什么区别?
原文链接:tf中的padding方式SAME和VALID有什么区别? 转载于:https://www.cnblogs.com/exciting/p/11363489.html
最新文章
- mysql 不同分区 同时insert_一文看懂mysql数据库分区表概念、类型、适用场景、优缺点及原理...
- “达观杯”NLP算法大赛再启航,丰厚奖金、直通Offer等你来拿!
- 一文读懂5G:颠覆生活资费天价?
- 编程异常——假设你报createSQLQuery is not valid without active transaction,...
- spark context stop use with as
- 下一个排列Python解法
- c++11 thread类的简单使用
- 使用脚本删除ios工程中未使用图片
- K8S认证、授权与准入控制(RBAC)详解
- 微信小程序——风水罗盘
- 关于个人博客的相关内容
- Apache Log4j2.x RCE命令执行漏洞攻击原理及修复措施
- HTML(hiden控件 readonly disabled)(maxlength属性)(id属性重点)
- 风险中性的深度学习选股策略
- 自动切换输入法--Mac高效切换输入法工具
- 你不可不知的《哈利波特》秘密(五)
- Ubuntu下docker相关问题 - 持续更新中
- Visual studio2022还原VC6环境设置
- 基于Java的项目人力资源管理系统【附:源码课件】
- matlab抛物偏微分方程,抛物型方程差分求解 跪求MATLAB解抛物型偏微分方程的程序...
热门文章
- H264 over RTP中存在多个SPS、PPS、[SEI]、 IDR序列是否都需要
- Python实现冒泡排序
- 运放专题:运放电路振铃产生的原因及解决办法
- A股月份效应 | Python量化A股市场魔咒,5穷6绝7翻身准确吗?| 邢不行
- 走向共同富裕,互联网大厂还属腾讯觉悟最高!
- 同一个局域网怎么计算机共享打印机,如何共享同一个局域网中的打印机
- 【渝粤教育】电大中专学前儿童语言教育 (2)作业 题库
- Linux网络实战- FTP服务器搭建
- LightGBM介绍
- 使用GridMaps和Godot及Blender设计游戏场景视频教程