针对valid和invalid伪类美化表单案例这个问题,本篇文章进行了详细的解答,同时本文还将给你拓展@Validated和@Valid校验参数、级联属性、List、@Validated和@Valid
针对valid和invalid伪类美化表单案例这个问题,本篇文章进行了详细的解答,同时本文还将给你拓展@Validated和@Valid校验参数、级联属性、List、@Validated和@Valid的区别?校验级联属性(内部类)、@Valid和@Validated的总结区分(转)、Android中invalidate()和postInvalidate() 的区别及使用方法等相关知识,希望可以帮助到你。
本文目录一览:- valid和invalid伪类美化表单案例
- @Validated和@Valid校验参数、级联属性、List
- @Validated和@Valid的区别?校验级联属性(内部类)
- @Valid和@Validated的总结区分(转)
- Android中invalidate()和postInvalidate() 的区别及使用方法
valid和invalid伪类美化表单案例
效果图
<!DOCTYPE html> <html lang="en"head> Meta charset="UTF-8"title>form</style> .container{margin:100px;position relativefont-size14px} inputwidth140pxheight30pxline-heightborder1px solid #999outlinenonetext-indent40pxtransitionall .3sborder-radius 3px .title absolutetop1pxleft2px 30px input:focus,input:hover4px input:focus+.title,input:hover+.titletransformtranslateX(-120%) input:valid~label:aftercontent"邮箱格式正确"colorgreen input:invalid~label:after"邮箱格式不正确"red input:valid1px solid green input:invalid1px solid red} bodydiv class="container"> form action="#"> input type="email" id required placeholder="输入邮箱"span ="title">邮箱spanlabel for="email"></labelformdiv> html>
补充:
::after 和 :after是一样的效果,两个冒号语义更明确,但一个冒号兼容性更好
总结
以上是小编为你收集整理的valid和invalid伪类美化表单案例全部内容。
如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。
@Validated和@Valid校验参数、级联属性、List
@Validated和@Valid的区别
在Controller中校验方法参数时,使用@Valid和@Validated并无特殊差异(若不需要分组校验的话): @Valid:标准JSR-303规范的标记型注解,用来标记验证属性和方法返回值,进行级联和递归校验 @Validated:Spring的注解,是标准JSR-303的一个变种(补充),提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制
方法级别: @Validated注解可以用于类级别,用于支持Spring进行方法级别的参数校验。@Valid可以用在属性级别约束,用来表示级联校验。 @Validated只能用在类、方法和参数上,而@Valid可用于方法、字段、构造器和参数上
校验参数、级联属性
1、校验参数 当入参为实体对象时,需要在方法上加@Valid或@Validated或者在参数前加@Valid或@Validated,或者在类上加@Validated
@Valid @GetMapping("/exam-info") public Boolean getInfo(User user){......}
@GetMapping("/exam-info") public Boolean getInfo(@Valid User user){......}
@Validated @GetMapping("/exam-info") public Boolean getInfo(User user){......}
@GetMapping("/exam-info") public Boolean getInfo(@Validated User user){......}
public Class User{ @NotNull("id不能为空") private Integer id; }
2、嵌套验证 @valid作用于属性上有嵌套验证作用,@validated不能作用于属性上,如下代码在User类的属性car上添加@valid注解,当传参id为空时会报错。
@GetMapping("/exam-info") public Boolean getInfo(@Valid User user){.....}
public class User{ @Valid @NotNull("car不能为空") private Car car; } public class Car{ @NotNull("id不能为空") private Integer id; }
校验List
@Valid只能校验JavaBean,而List<E>不是JavaBean所以校验会失败,介绍三种解决办法
方法1:对List进行Wrapper
既然List不是JavaBean,那我们就把它封装成JavaBean,我们定义一个ListWrapper类如下:
import lombok.Getter; import lombok.Setter; import javax.validation.Valid; import java.util.ArrayList; import java.util.List;
@Setter @Getter public class ListWrapper<E> { @Valid private List<E> list;
public ListWrapper() {
list = new ArrayList<>();
}
public ListWrapper(List<E> list) {
this.list = list;
}
} 同时修改一下controller对应的方法:
// 使用包装类对list进行验证
@PostMapping("/insert/all")
public ServerResponse<String> insertList(@Valid @RequestBody ListWrapper<UserEntity> listWrapper, BindingResult bindingResult) {
if(bindingResult.hasErrors()) {
log.error(bindingResult.getFieldError().toString());
return ServerResponse.createByErrorMessage(bindingResult.getFieldError().getDefaultMessage());
}
userService.insertList(listWrapper.getList());
return ServerResponse.createBySuccess();
}
这样就可以对list进行校验了
注意:
由于对list进行了包装,如果我们传参的时候
[{},{}..]要改为{“list”: [{},{}..]}
方法2:使用@Validated+@Valid
在controller类上面增加@Validated注解,并且删除方法参数中的BindingResult bindingResult(因为这个参数已经没有用了,异常统一有controller返回了)
然后我们运行一下测试一下
可以看到可以对参数进行校验了,但还还有一个问题,那就是这个不是我们想要的返回格式,它controller自己返回的格式,所以我们需要做一个统一异常处理,代码如下:
import com.wyq.firstdemo.common.ServerResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import java.util.Set;
@Slf4j @RestControllerAdvice public class ControllerExceptionHandler {
@ExceptionHandler
@ResponseBody
public ServerResponse<String> handle(ConstraintViolationException exception) {
log.error(String.valueOf(exception));
Set<ConstraintViolation<?>> violations = exception.getConstraintViolations();
StringBuilder builder = new StringBuilder();
for (ConstraintViolation violation : violations) {
builder.append(violation.getMessage());
break;
}
return ServerResponse.createByErrorMessage(builder.toString());
}
} 经过统一异常处理,我们这边的返回结果就是我们想要的格式了
方法3:自定义一个List
先上代码后说明,先定义一个ValidList
import javax.validation.Valid; import java.util.*;
public class ValidList<E> implements List<E> {
@Valid
private List<E> list;
public ValidList() {
this.list = new ArrayList<>();
}
public ValidList(List<E> list) {
this.list = list;
}
public List<E> getList() {
return list;
}
public void setList(List<E> list) {
this.list = list;
}
@Override
public int size() {
return list.size();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
...
} 对比方法3和方法1,有没有觉得代码有点相似,新建一个类,并且让他实现List接口,使这个类即具有了JavaBean的特性,又具有了List的特性,比方法1简单优雅很多。
只需要把List换成ValidList就可以了,还不需要多统一异常处理。
参考:https://my.oschina.net/fangshixiang/blog/3081366 https://stackoverflow.com/questions/28150405/validation-of-a-list-of-objects-in-spring/36313615#36313615 https://my.oschina.net/fangshixiang/blog/3081366
本文由博客一文多发平台 OpenWrite 发布!
@Validated和@Valid的区别?校验级联属性(内部类)
每篇一句
NBA里有两大笑话:一是科比没天赋,二是詹姆斯没技术
相关阅读
【小家Java】深入了解数据校验:Java Bean Validation 2.0(JSR303、JSR349、JSR380)Hibernate-Validation 6.x使用案例
【小家Spring】让Controller支持对平铺参数执行数据校验(默认Spring MVC使用@Valid只能对JavaBean进行校验)
【小家Spring】Spring方法级别数据校验:@Validated + MethodValidationPostProcessor优雅的完成数据校验动作
<center>对Spring感兴趣可扫码加入wx群:Java高工、架构师3群
(文末有二维码)</center>
前言
上篇文章 介绍了Spring
环境下实现优雅的方法级别的数据校验,并且埋下一个伏笔:它在Spring MVC
(Controller
层)里怎么应用呢?本文为此继续展开讲解Spring MVC
中的数据校验~
可能小伙伴能立马想到:这不一样吗?我们使用Controller
就是方法级别的,所以它就是直接应用了方法级别的校验而已嘛~对于此疑问我先不解答,而是顺势再抛出两个问题你自己应该就能想明白了:
-
上文有说过,基于方法级别的校验
Spring
默认是并未开启
的,但是为什么你在Spring MVC
却可以直接使用@Valid
完成校验呢?1. 可能有的小伙伴说他用的是`SpringBoot`可能默认给开启了,其实不然。哪怕你用的传统`Spring MVC`你会发现也是直接可用的,不信你就试试
- 类比一下:
Spring MVC
的HandlerInterceptor
是AOP
思想的实现,但你有没有发现即使你没有启动@EnableAspectJAutoProxy
的支持,它依旧好使~
若你能想明白我提出的这两个问题,下文就非常不难理解了。当然即使你知道了这两个问题的答案,还是建议你读下去。毕竟:永远相信本文能给你带来意想不到的收获~
使用示例
关于数据校验这一块在Spring MVC
中的使用案例,我相信但凡有点经验的Java程序员应该没有不会使用的,并且还不乏熟练的选手。在此之前我简单“采访”
过,绝大多数程序员甚至一度认为Spring中的数据校验就是指的在Controller
中使用@Validated
校验入参JavaBean
这一块~
因此下面这个例子,你应该一点都不陌生:
@Getter
@Setter
@ToString
public class Person {
@NotNull
private String name;
@NotNull
@Positive
private Integer age;
@Valid // 让InnerChild的属性也参与校验
@NotNull
private InnerChild child;
@Getter
@Setter
@ToString
public static class InnerChild {
@NotNull
private String name;
@NotNull
@Positive
private Integer age;
}
}
@RestController
@RequestMapping
public class HelloController {
@PostMapping("/hello")
public Object helloPost(@Valid @RequestBody Person person, BindingResult result) {
System.out.println(result.getErrorCount());
System.out.println(result.getAllErrors());
return person;
}
}
发送post请求:/hello
Content-Type=application/json
,传入的json串如下:
{
"name" : "fsx",
"age" : "-1",
"child" : {
"age" : 1
}
}
控制台有如下打印:
2
[Field error in object ''person'' on field ''child.name'': rejected value [null]; codes [NotNull.person.child.name,NotNull.child.name,NotNull.name,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [person.child.name,child.name]; arguments []; default message [child.name]]; default message [不能为null], Field error in object ''person'' on field ''age'': rejected value [-1]; codes [Positive.person.age,Positive.age,Positive.java.lang.Integer,Positive]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [person.age,age]; arguments []; default message [age]]; default message [必须是正数]]
从打印上看:校验生效(拿着错误消息就可以返回前端展示,或者定位到错误页面都行)。
此例两个小细节务必注意:
-
@RequestBody
注解不能省略,否则传入的json无法完成数据绑定(即使不绑定,校验也是生效的哦)~ - 若方法入参不写
BindingResult result
这个参数,请求得到的直接是400错误,因为若有校验失败的服务端会抛出异常org.springframework.web.bind.MethodArgumentNotValidException
。若写了,那就调用者自己处理喽~
据我不完全和不成熟的统计,就这个案例就覆盖了小伙伴们实际使用中的90%
以上的真实使用场景,使用起来确实非常的简单、优雅、高效~
但是作为一个有丰富经验的程序员的你,虽然你使用了@Valid
优雅的完成了数据校验,但回头是你是否还会发现你的代码里还是存在了大量的if else
的基础的校验?什么原因?其实根本原因只有一个:很多case使用@Valid并不能覆盖,因为它只能校验JavaBean
我相信你是有这样那样的使用痛点的,本文先从原理层面分析,进而给出你所遇到的痛点问题的参考解决参考方案~
原理分析
Controller
提供的使用@Valid
便捷校验JavaBean
的原理,和Spring方法级别的校验支持的原理是有很大差异的(可类比Spring MVC
拦截器和Spring AOP
的差异区别~),那么现在就看看这块吧
请不要忽视优雅
代码的力量,它会成倍提升你的编码效率、成倍降低后期维护成本,甚至成倍提升你的扩展性和成倍降低你写bug的可能性~
回忆DataBinder
/WebDataBinder
若对Spring
数据绑定模块不是很熟悉的(有阅读过我之前文章的可忽略),建议先补:
- 【小家Spring】聊聊Spring中的数据绑定 --- DataBinder本尊(源码分析)
- 【小家Spring】聊聊Spring中的数据绑定 --- WebDataBinder、ServletRequestDataBinder、WebBindingInitializer...
DataBinder
类名叫数据绑定,但它在org.springframework.validation
这个包,可见Spring它把数据绑定和数据校验牢牢的放在了一起,并且内部弱化了数据校验的概念以及逻辑(Spring
想让调用者无需关心数据校验的细节,全由它来自动完成,减少使用的成本)。
我们知道DataBinder
它主要对外提供了bind(PropertyValues pvs)
和validate()
方法,当然还有处理绑定/校验失败的相关(配置)组件:
public class DataBinder implements PropertyEditorRegistry, TypeConverter {
...
@Nullable
private AbstractPropertyBindingResult bindingResult; // 它是个BindingResult
@Nullable
private MessageCodesResolver messageCodesResolver;
private BindingErrorProcessor bindingErrorProcessor = new DefaultBindingErrorProcessor();
// 最重要是它:它是org.springframework.validation.Validator
// 一个DataBinder 可以持有对个验证器。也就是说对于一个Bean,是可以交给多个验证器去验证的(当然一般都只有一个即可而已~~~)
private final List<Validator> validators = new ArrayList<>();
public void bind(PropertyValues pvs) {
MutablePropertyValues mpvs = (pvs instanceof MutablePropertyValues ? (MutablePropertyValues) pvs : new MutablePropertyValues(pvs));
doBind(mpvs);
}
...
public void validate() {
Object target = getTarget();
Assert.state(target != null, "No target to validate");
BindingResult bindingResult = getBindingResult();
// 拿到所有的验证器 一个个的对此target进行验证~~~
// Call each validator with the same binding result
for (Validator validator : getValidators()) {
validator.validate(target, bindingResult);
}
}
}
DataBinder
提供了这两个非常独立的原子方法:绑定 + 校验。他俩结合完成了我们的数据绑定+数据校验,完全的和业务无关~
网上有很多文章说DataBinder
完成数据绑定后继续校验,这种说法是不准确的呀,因为它并不处理这部分的组合逻辑,它只提供原始能力~
Spring MVC
处理入参的时机
Spring MVC
处理入参的逻辑是非常复杂的,前面花大篇幅讲了Spring MVC
对返回值的处理器:HandlerMethodReturnValueHandler
,详见:
【小家Spring】Spring MVC容器的web九大组件之---HandlerAdapter源码详解---一篇文章带你读懂返回值处理器HandlerMethodReturnValueHandler
同样的,本文只关注它对@RequestBody
这种类型的入参进行讲解~
处理入参的处理器:HandlerMethodArgumentResolver
,处理@RequestBody
最终使用的实现类是:RequestResponseBodyMethodProcessor
,Spring
借助此处理器完成一系列的消息转换器、数据绑定、数据校验等工作~
RequestResponseBodyMethodProcessor
这个类应该是陌生的,在上面推荐的处理MVC返回值的文章中有提到过它:它能够处理@ResponseBody
注解返回值(请参考它的supportsReturnType()
方法~)
它还有另一个能力是:它能够处理请求参数(当然也是标注了@RequestBody
它的~)
所以它既是个处理返回值的HandlerMethodReturnValueHandler
,有是一个处理入参的HandlerMethodArgumentResolver
。所以它命名为Processor
而不是Resolver/Handler
嘛,这就是命名的艺术~
// @since 3.1
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
// 类上或者方法上标注了@ResponseBody注解都行
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class));
}
// 这是处理入参封装校验的入口,也是本文关注的焦点
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 它是支持`Optional`容器的
parameter = parameter.nestedIfOptional();
// 使用消息转换器HttpInputMessage把request请求转换出来
// 此处注意:比如本例入参是Person类,所以经过这里处理会生成一个空的Person对象出来(反射)
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
// 获取到入参的名称
// 请注意:这里的名称是类名首字母小写,并不是你方法里写的名字。比如本利若形参名写为personAAA,但是name的值还是person
String name = Conventions.getVariableNameForParameter(parameter);
// 只有存在binderFactory才会去完成自动的绑定、校验~
// 此处web环境为:ServletRequestDataBinderFactory
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
// 显然传了参数才需要去绑定校验嘛
if (arg != null) {
// 这里完成数据绑定+数据校验~~~~~(绑定的错误和校验的错误都会放进Errors里)
// Applicable:适合
validateIfApplicable(binder, parameter);
// 若有错误消息hasErrors(),并且仅跟着的一个参数不是Errors类型,Spring MVC会主动给你抛出MethodArgumentNotValidException异常
// 否则,调用者自行处理
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
// 把错误消息放进去 证明已经校验出错误了~~~
// 后续逻辑会判断MODEL_KEY_PREFIX这个key的~~~~
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
return adaptArgumentIfNecessary(arg, parameter);
}
// 校验,如果合适的话。使用WebDataBinder,失败信息最终也都是放在它身上~ 本方法是本文关注的焦点
// 入参:MethodParameter parameter
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
// 拿到标注在此参数上的所有注解们(比如此处有@Valid和@RequestBody两个注解)
Annotation[] annotations = parameter.getParameterAnnotations();
for (Annotation ann : annotations) {
// 先看看有木有@Validated
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
// 这个里的判断是关键:可以看到标注了@Validated注解 或者注解名是以Valid打头的 都会有效哦
//注意:这里可没说必须是@Valid注解。实际上你自定义注解,名称只要一Valid开头都成~~~~~
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
// 拿到分组group后,调用binder的validate()进行校验~~~~
// 可以看到:拿到一个合适的注解后,立马就break了~~~
// 所以若你两个主机都标注@Validated和@Valid,效果是一样滴~
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
binder.validate(validationHints);
break;
}
}
}
...
}
本文我们只着眼于关注@Valid
的数据校验这块,有几个相关的使用小细节,总结如下:
- 形参(
@RequestBody
标注的入参)的name(形参名)
和你写什么无关,若是实体类名字就是类名首字母小写。(若是数组、集合等,都会有自己特定的名称) -
@Validated
和@Valid
都能使校验生效,但却不仅仅是它哥俩才能行:任何名称是"Valid"打头的注解都能使得数据校验生效1. 自定义注解名称以`Valid`开头,并且给个`value`属性同样能够指定`Group`分组 2. 个人直接建议使用`@Validated`即可,而去使用`@Valid`了,更不用自己给自己找麻烦去自定义注解啥的了~
- 只有当
Errors(BindingResult)
入参是是仅跟着@Valid
注解的实体,Spring MVC
才会把错误消息放权交给调用者处理,否则(没有或者不是紧挨着)它会抛出MethodArgumentNotValidException
异常~
这是使用@RequestBody
结合@Valid
完成数据校验的基本原理。其实当Spring MVC
在处理@RequestPart
注解入参数据时,也会执行绑定、校验的相关逻辑。对应处理器是RequestPartMethodArgumentResolver
,原理大体上和这相似,它主要处理Multipart
相关,本文忽略~
==此处提示一个点,此文发出去后有一个好奇的小宝宝问我入参能使用多个对象并且都用@RequestBody
标注吗?==
关于这个问题姑且先不考虑合理与否,我们这样做试试:
@PostMapping("/hello")
public Object helloPost(@Valid @RequestBody Person personAAA, BindingResult result, @Valid @RequestBody Person personBBB) {
...
}
请求却报错了:
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: I/O error while reading input message; nested exception is java.io.IOException: Stream closed]
错误消息很好理解:请求域的Body体是只能被读取一次的(流只能被读取一次嘛)。
若你是好奇的,你可能还会问:URL参数呢?请求链接?
后面的参数呢,如何封装???因为本部分内容不是本文的关注点,若有兴趣请出门左拐~
说明:关于使用Map、List、数组等接受@RequestBody
参数的情况类似,区别在于绑定器上,对Map、List的校验前面文章有过讲解,此处就不展开了。希望读者能掌握这部分内容,因为它和面向使用者比较重要的
@InitBinder
强关联~~~
实际使用中一般使用@Validated
分组校验(若需要),然后结合全局异常的处理方式来友好的对调用者展示错误消息~
全局异常处理示例
当校验失败时,Spring
会抛出MethodArgumentNotValidException
异常,该异常会持有校验结果对象BindingResult
,从而获得校验失败信息。本处只给示例,仅供参考:
@RestControllerAdvice
public class MethodArgumentNotValidExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
BindingResult bindingResult = ex.getBindingResult();
StringBuilder stringBuilder = new StringBuilder();
for (FieldError error : bindingResult.getFieldErrors()) {
String field = error.getField();
Object value = error.getRejectedValue();
String msg = error.getDefaultMessage();
String message = String.format("错误字段:%s,错误值:%s,原因:%s;", field, value, msg);
stringBuilder.append(message).append("\r\n");
}
return Result.error(MsgDefinition.ILLEGAL_ARGUMENTS.codeOf(), stringBuilder.toString());
}
}
遗留痛点
你是否发现,虽然Spring MVC
给我们提供了极其方便的数据校验方式,但是它还是有比较大的局限性的:它要求待校验的入参是JavaBean
请注意:并不一样要求是请求Body体哦,比如get请求的入参若用JavaBean接收的话,依旧能启用校验
但在实际应用中,其实我们非常多的Controller
方法的方法入参是平铺的
,也就是所谓的平铺参数
,形如这样:
@PutMapping("/hello/id/{id}/status/{status}")
public Object helloGet(@PathVariable Integer id, @PathVariable Integer status) {
...
return "hello world";
}
其实,特别是get请求的case,@RequestParam
入参一般是非常多的(比如分页查询),难道对于这种平铺参数的case,我们真的是能通过人肉if else
的去校验吗?
兴许你对此问题有兴趣,那就参阅本文吧,它能给你提供解决方案:【小家Spring】让Controller支持对平铺参数执行数据校验(默认Spring MVC使用@Valid只能对JavaBean进行校验)
==@Validated和@Valid的区别==
如题的问题,我相信是很多小伙伴都很关心的一个对比,若你把这个系列都有喵过,那么这个问题的答案就浮出水面了:
-
@Valid
:标准JSR-303规范的标记型注解,用来标记验证属性和方法返回值,进行级联和递归校验 -
@Validated
:Spring
的注解,是标准JSR-303
的一个变种(补充),提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制 - 在
Controller
中校验方法参数时,使用@Valid和@Validated并无特殊差异(若不需要分组校验的话) -
@Validated
注解可以用于类级别,用于支持Spring进行方法级别的参数校验。@Valid
可以用在属性级别约束,用来表示级联校验。 -
@Validated
只能用在类、方法和参数上,而@Valid
可用于方法、字段、构造器和参数上
最后提示一点:Spring Boot
的Web Starter
已经加入了Bean Validation
以及实现的依赖,可以直接使用。但若是纯Spring MVC
环境,请自行导入~
总结
本文介绍的是我们平时使用得最多的数据校验场景:使用@Validated
完成Controller
的入参校验,实现优雅的处理数据校验。同时希望通过本文能让你彻底弄懂@Validated和@Valid
使用上的区别以及联系,在实际生产使用中能够做到更加的得心应手~
知识交流
若文章格式混乱,可点击
:原文链接-原文链接-原文链接-原文链接-原文链接
==The last:如果觉得本文对你有帮助,不妨点个赞呗。当然分享到你的朋友圈让更多小伙伴看到也是被作者本人许可的~
==
**若对技术内容感兴趣可以加入wx群交流:Java高工、架构师3群
。
若群二维码失效,请加wx号:fsx641385712
(或者扫描下方wx二维码)。并且备注:"java入群"
字样,会手动邀请入群**
@Valid和@Validated的总结区分(转)
@Valid:
@Valid注解用于校验,所属包为:javax.validation.Valid。
① 首先需要在实体类的相应字段上添加用于充当校验条件的注解,如:@Min,如下代码(age属于Girl类中的属性):
@Min(value = 18,message = "未成年禁止入内")
private Integer age;
② 其次在controller层的方法的要校验的参数上添加@Valid注解,并且需要传入BindingResult对象,用于获取校验失败情况下的反馈信息,如下代码:
@PostMapping("/girls")
public Girl addGirl(@Valid Girl girl, BindingResult bindingResult) {
if(bindingResult.hasErrors()){
System.out.println(bindingResult.getFieldError().getDefaultMessage());
return null;
}
return girlResposity.save(girl);
}
bindingResult.getFieldError.getDefaultMessage()用于获取相应字段上添加的message中的内容,如:@Min注解中message属性的内容
@Validated:
@Valid是javax.validation里的。
@Validated是@Valid 的一次封装,是Spring提供的校验机制使用。@Valid不提供分组功能
@Validated的特殊用法
1、分组
当一个实体类需要多种验证方式时,例:对于一个实体类的id来说,新增的时候是不需要的,对于更新时是必须的。
可以通过groups对验证进行分组
分组接口类(通过向groups分配不同类的class对象,达到分组目的):
package com.valid.interfaces;
public interface First {
}
实体类:
package com.valid.pojo;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotEmpty;
import com.valid.interfaces.First;
public class People {
//在First分组时,判断不能为空
@NotEmpty(groups={First.class})
private String id;
//name字段不为空,且长度在3-8之间
@NotEmpty
@Size(min=3,max=8)
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
注:
(1)不分配groups,默认每次都要进行验证
(2)对一个参数需要多种验证方式时,也可通过分配不同的组达到目的。例:
@NotEmpty(groups={First.class})
@Size(min=3,max=8,groups={Second.class})
private String name;
控制类:
package com.valid.controller;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.valid.interfaces.First;
import com.valid.pojo.People;
@Controller
public class FirstController {
@RequestMapping("/addPeople")
//不需验证ID
public @ResponseBody String addPeople(@Validated People p,BindingResult result)
{
System.out.println("people''s ID:" + p.getId());
if(result.hasErrors())
{
return "0";
}
return "1";
}
@RequestMapping("/updatePeople")
//需要验证ID
public @ResponseBody String updatePeople(@Validated({First.class}) People p,BindingResult result)
{
System.out.println("people''s ID:" + p.getId());
if(result.hasErrors())
{
return "0";
}
return "1";
}
}
注:
@Validated没有添加groups属性时,默认验证没有分组的验证属性,如该例子:People的name属性。
@Validated没有添加groups属性时,所有参数的验证类型都有分组(即本例中People的name的@NotEmpty、@Size都添加groups属性),则不验证任何参数
2、组序列
默认情况下,不同组别的约束验证是无序的,然而在某些情况下,约束验证的顺序却很重要。
例:
(1)第二个组中的约束验证依赖于一个稳定状态来运行,而这个稳定状态是由第一个组来进行验证的。
(2)某个组的验证比较耗时,CPU 和内存的使用率相对比较大,最优的选择是将其放在最后进行验证。因此,在进行组验证的时候尚需提供一种有序的验证方式,这就提出了组序列的概念。
一个组可以定义为其他组的序列,使用它进行验证的时候必须符合该序列规定的顺序。在使用组序列验证的时候,如果序列前边的组验证失败,则后面的组将不再给予验证。
分组接口类 (通过@GroupSequence注解对组进行排序):
package com.valid.interfaces;
public interface First {
}
package com.valid.interfaces;
public interface Second {
}
package com.valid.interfaces;
import javax.validation.GroupSequence;
@GroupSequence({First.class,Second.class})
public interface Group {
}
实体类:
package com.valid.pojo;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotEmpty;
import com.valid.interfaces.First;
import com.valid.interfaces.Second;
public class People {
//在First分组时,判断不能为空
@NotEmpty(groups={First.class})
private String id;
//name字段不为空,且长度在3-8之间
@NotEmpty(groups={First.class})
@Size(min=3,max=8,groups={Second.class})
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
3、验证多个对象
一个功能方法上处理多个模型对象时,需添加多个验证结果对象
package com.valid.controller;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.valid.pojo.People;
import com.valid.pojo.Person;
@Controller
public class FirstController {
@RequestMapping("/addPeople")
public @ResponseBody String addPeople(@Validated People p,BindingResult result,@Validated Person p2,BindingResult result2)
{
if(result.hasErrors())
{
return "0";
}
if(result2.hasErrors())
{
return "-1";
}
return "1";
}
}
————————————————
版权声明:本文为CSDN博主「高高程序猿」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/gaojp008/article/details/80583301
Android中invalidate()和postInvalidate() 的区别及使用方法
Android中实现view的更新有两组方法,一组是invalidate,另一组是postInvalidate,其中前者是在UI线程自身中使用,而后者在非UI线程中使用。
Android提供了Invalidate方法实现界面刷新,但是Invalidate不能直接在线程中调用,因为他是违背了单线程模型:Android UI操作并不是线程安全的,并且这些操作必须在UI线程中调用。
invalidate()是用来刷新View的,必须是在UI线程中进行工作。比如在修改某个view的显示时,调用invalidate()才能看到重新绘制的界面。invalidate()的调用是把之前的旧的view从主UI线程队列中pop掉。 一个Android 程序默认情况下也只有一个进程,但一个进程下却可以有许多个线程。
在这么多线程当中,把主要是负责控制UI界面的显示、更新和控件交互的线程称为UI线程,由于onCreate()方法是由UI线程执行的,所以也可以把UI线程理解为主线程。其余的线程可以理解为工作者线程。
invalidate()得在UI线程中被调动,在工作者线程中可以通过Handler来通知UI线程进行界面更新;而postInvalidate()在工作者线程中被调用。
利用invalidate()刷新界面
实例化一个Handler对象,并重写handleMessage方法调用invalidate()实现界面刷新;而在线程中通过sendMessage发送界面更新消息。
// 在onCreate()中开启线程 new Thread( new GameThread()).start();、 // 实例化一个handler Handler myHandler = new Handler() { // 接收到消息后处理 public void handleMessage(Message msg) { switch (msg.what) { case Activity01.REFRESH: mGameView.invalidate(); // 刷新界面 break ; } super .handleMessage(msg); } }; class GameThread implements Runnable { public void run() { while (!Thread.currentThread().isInterrupted()) { Message message = new Message(); message.what = Activity01.REFRESH; // 发送消息 Activity01.this .myHandler.sendMessage(message); try { Thread.sleep(100 ); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } } // 在onCreate()中开启线程 new Thread(new GameThread()).start();、 // 实例化一个handler Handler myHandler = new Handler() { // 接收到消息后处理 public void handleMessage(Message msg) { switch (msg.what) { case Activity01.REFRESH: mGameView.invalidate(); // 刷新界面 break; } super.handleMessage(msg); } }; class GameThread implements Runnable { public void run() { while (!Thread.currentThread().isInterrupted()) { Message message = new Message(); message.what = Activity01.REFRESH; // 发送消息 Activity01.this.myHandler.sendMessage(message); try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } }
使用postInvalidate()刷新界面
使用postInvalidate则比较简单,不需要handler,直接在线程中调用postInvalidate即可。
class GameThread implements Runnable { public void run() { while (!Thread.currentThread().isInterrupted()) { try { Thread.sleep(100 ); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // 使用postInvalidate可以直接在线程中更新界面 mGameView.postInvalidate(); } } }
以上所述是小编给大家分享的Android中invalidate()和postInvalidate() 的区别及使用方法,希望对大家有所帮助!
关于valid和invalid伪类美化表单案例的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于@Validated和@Valid校验参数、级联属性、List、@Validated和@Valid的区别?校验级联属性(内部类)、@Valid和@Validated的总结区分(转)、Android中invalidate()和postInvalidate() 的区别及使用方法等相关知识的信息别忘了在本站进行查找喔。
本文标签: