芋道 Spring Boot 参数校验 Validation 入门

Spring Boot 项目中参数校验和异常拦截 比如前端提交的某个参数必须是枚举值中的一个,我们尝试用自定义注解来完成该校验逻辑。定义校验注解String message() default "必须指定颜色类型";// 所有允许的颜色值Class 阅读详情

点击上方“芋道源码”,选择“设为星标”

做积极的人,而不是积极废人!

源码精品专栏

 

摘要: 原创出处 http://www.iocoder.cn/Spring-Boot/Validation/ 「芋道源码」欢迎转载,保留摘要,谢谢!

  • 1. 概述

  • 2. 注解

  • 3. 快速入门

  • 4. 处理校验异常

  • 5. 自定义约束

  • 6. 分组校验

  • 7. 手动校验

  • 8. 国际化 i18n

  • 666. 彩蛋


本文在提供完整代码示例,可见 https://github.com/YunaiV/SpringBoot-Labs 的 lab-22 目录。

原创不易,给点个 Star 嘿,一起冲鸭!

1. 概述

在想标题的时候,到底应该叫数据校验,还是参数校验时,我纠结了,而且非常。

最后,考虑参数校验更贴近我们的理解,就选择了它。实际更合适的叫法,还是数据校验。

文头艿艿瞎哔哔了一些碎碎念,嫌弃的胖友,可以跳往 「3. 快速入门」 。

当我们想提供可靠的 API 接口,对参数的校验,以保证最终数据入库的正确性,是必不可少的活。例如说,用户注册时,会校验手机格式的正确性,密码非弱密码。

可惜的是,在翻开自己的项目的时候,会发现大量的 API 接口,我们并没有添加相应的参数校验,而是把这个活交给调用方(例如说前端)来完成。???? 甚至在艿艿接触过的后端开发中,认为这是前端的活,简直了!

世界比我们想象中的不安全,可能有“黑客”会绕过浏览器,直接使用 HTTP 工具,模拟请求向后端 API 接口传入违法的参数,以达到它们“不可告人”的目的。

又或者前端开发小哥,不小心漏做了一些 API 接口调用时的参数校验,结果导致用户提交了大量不正确的数据到后端 API 接口,并且这些数据成功入库了。这个时候,你是会甩锅给前端小哥,还是怒喷测试小姐姐验收不到位呢?

我相信,很多时候并不是我们不想添加,而是没有统一方便的方式,让我们快速的添加实现参数校验的功能。毕竟,比起枯燥的 CRUD 来说,它更枯燥。例如说,还是拿用户注册的接口,校验手机和密码这两个参数,可能就要消耗掉小 10 行的代码。更不要说,管理后台创建商品这种参数贼多的接口。

???? 世界上大多数碰到的困难,大多已经有了解决方案,特别是软件开发。实际上,Java 早在 2009 年就提出了 Bean Validation 规范,并且已经历经 JSR303、JSR349、JSR380 三次标准的置顶,发展到了 2.0 。

FROM https://beanvalidation.org/specification/

Bean Validation 1.0 :Bean Validation 1.0 (JSR 303) was the first version of Java's standard for object validation. It was released in 2009 and is part of Java EE 6. You can learn more about Bean Validation 1.0 here (specification text, API docs etc).

Bean Validation 1.1 :Bean Validation 1.1 (JSR 349) was finished in 2013 and is part of Java EE 7. Its main contributions are method-level validation, integration with CDI, group conversion and some more. You can learn more about Bean Validation 1.1 here (specification text, full change log, API docs etc).

Bean Validation 2.0 :Bean Validation 2.0 (JSR 380) was finished in August 2017.

It's part of Java EE 8 (but can of course be used with plain Java SE as the previous releases).

You can learn more about Bean Validation 2.0 here (specification text, full change log, API docs etc).

Bean Validation 和我们很久以前学习过的 JPA 一样,只提供规范,不提供具体的实现。

艿艿:对 JPA 不了的胖友,可以看看 《芋道 Spring Boot JPA 入门》 一文。

  • 在 Bean Validation API 中,定义了 Bean Validation 相关的接口,并没有具体实现。

  • 在 javax.validation.constraints 包下,定义了一系列的校验注解。例如说,@NotNull、@NotEmpty 。

实现 Bean Validation 规范的数据校验框架,主要有:

  • Hibernate Validator

    不要以为 Hibernate 仅仅是一个 ORM 框架,这只是它的 Hibernate ORM 所提供的。

    Hibernate 可是打着“Everything data”口号的,它还提供了 Hibernate Search、Hibernate OGM 等等解决方案的。????

    所以,女朋友也是 data ,我们来 new 一个就好,不需要找。

  • ???? 咳咳咳,突然想不起来还有个叫啥了,以后补充吧。啪啪打脸的疼~ Apache BVal

绝大多数情况下,也就 99.99% 吧,我们采用 Hibernate Validator 。

但是,我们在使用 Spring 的项目中,因为 Spring Validation 提供了对 Bean Validation 的内置封装支持,可以使用 @Validated 注解,实现声明式校验,而无需直接调用 Bean Validation 提供的 API 方法。而在实现原理上,也是基于 Spring AOP 拦截,实现校验相关的操作。

友情提示:这一点,类似 Spring Transaction 事务,通过 @Transactional 注解,实现声明式事务。

而在 Spring Validation 内部,最终还是调用不同的 Bean Validation 的实现框架。例如说,Hibernate Validator 。

下面,让我们开始遨游,在 Spring Boot 中,如何实现参数校验。

2. 注解

在开始入门之前,我们先了解下本文可能会涉及到的注解。

2.1 Bean Validation 定义的约束注解

javax.validation.constraints 包下,定义了一系列的约束( constraint )注解。如下:

参考 《JSR 303 - Bean Validation 介绍及最佳实践》 博客。

一共 22 个注解,快速略过即可。

  • 空和非空检查

    • @NotBlank :只能用于字符串不为 null ,并且字符串 #trim() 以后 length 要大于 0 。

    • @NotEmpty :集合对象的元素不为 0 ,即集合不为空,也可以用于字符串不为 null 。

    • @NotNull :不能为 null 。

    • @Null :必须为 null 。

  • 数值检查

    • @DecimalMax(value) :被注释的元素必须是一个数字,其值必须小于等于指定的最大值。

    • @DecimalMin(value) :被注释的元素必须是一个数字,其值必须大于等于指定的最小值。

    • @Digits(integer, fraction) :被注释的元素必须是一个数字,其值必须在可接受的范围内。

    • @Positive :判断正数。

    • @PositiveOrZero :判断正数或 0 。

    • @Max(value) :该字段的值只能小于或等于该值。

    • @Min(value) :该字段的值只能大于或等于该值。

    • @Negative :判断负数。

    • @NegativeOrZero :判断负数或 0 。

  • Boolean 值检查

    • @AssertFalse :被注释的元素必须为 true 。

    • @AssertTrue :被注释的元素必须为 false 。

  • 长度检查

    • @Size(max, min) :检查该字段的 size 是否在 min 和 max 之间,可以是字符串、数组、集合、Map 等。

  • 日期检查

    • @Future :被注释的元素必须是一个将来的日期。

    • @FutureOrPresent :判断日期是否是将来或现在日期。

    • @Past :检查该字段的日期是在过去。

    • @PastOrPresent :判断日期是否是过去或现在日期。

  • 其它检查

    • @Email :被注释的元素必须是电子邮箱地址。

    • @Pattern(value) :被注释的元素必须符合指定的正则表达式。

2.2 Hibernate Validator 附加的约束注解

org.hibernate.validator.constraints 包下,定义了一系列的约束( constraint )注解。如下:

  • @Range(min=, max=) :被注释的元素必须在合适的范围内。

  • @Length(min=, max=) :被注释的字符串的大小必须在指定的范围内。

  • @URL(protocol=,host=,port=,regexp=,flags=) :被注释的字符串必须是一个有效的 URL 。

  • @SafeHtml :判断提交的 HTML 是否安全。例如说,不能包含 javascript 脚本等等。

  • ... 等等,就不一一列举了。

2.3 @Valid 和 @Validated

@Valid 注解,是 Bean Validation 所定义,可以添加在普通方法、构造方法、方法参数、方法返回、成员变量上,表示它们需要进行约束校验。

@Validated 注解,是 Spring Validation 锁定义,可以添加在类、方法参数、普通方法上,表示它们需要进行约束校验。同时,@Validated 有 value 属性,支持分组校验。属性如下:

// Validated.java

Class<?>[] value() default {};

对于初学的胖友来说,很容易搞混 @Valid 和 @Validated 注解。

① 声明式校验

Spring Validation 仅对 @Validated 注解,实现声明式校验。

② 分组校验

Bean Validation 提供的 @Valid 注解,因为没有分组校验的属性,所以无法提供分组校验。此时,我们只能使用 ``@Validated` 注解。

③ 嵌套校验

相比来说,@Valid 注解的地方,多了【成员变量】。这就导致,如果有嵌套对象的时候,只能使用 @Valid 注解。例如说:

// User.java
public class User {
    
    private String id;

    @Valid
    private UserProfile profile;

}

// UserProfile.java
public class UserProfile {

    @NotBlank
    private String nickname;

}
  • 如果不在 User.profile 属性上,添加 @Valid 注解,就会导致 UserProfile.nickname 属性,不会进行校验。

当然,@Valid 注解的地方,也多了【构造方法】和【方法返回】,所以在有这方面的诉求的时候,也只能使用 @Valid 注解。

???? 总结

总的来说,绝大多数场景下,我们使用 @Validated 注解即可。

而在有嵌套校验的场景,我们使用 @Valid 注解添加到成员属性上。

3. 快速入门

示例代码对应仓库:lab-22-validation-01 。

本小节,我们会实现在 Spring Boot 中,对 SpringMVC 的 Controller 的 API 接口参数,实现参数校验。

同时,因为我们在 Service 也会有参数校验的诉求,所以我们也会提供示例。

3.1 引入依赖

在 pom.xml 文件中,引入相关依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>lab-22-validation-01</artifactId>

    <dependencies>
        <!-- 实现对 Spring MVC 的自动化配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 保证 Spring AOP 相关的依赖包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
        </dependency>

        <!-- 方便等会写单元测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>
  • 具体每个依赖的作用,胖友自己认真看下艿艿添加的所有注释噢。

  • spring-boot-starter-web 依赖里,已经默认引入 hibernate-validator 依赖,所以本示例使用的是 Hibernate Validator 作为 Bean Validation 的实现框架。

在 Spring Boot 体系中,也提供了 spring-boot-starter-validation 依赖。在这里,我们并没有引入。为什么呢?该依赖的目的,重点也是引入 hibernate-validator 依赖,这在 spring-boot-starter-web 已经引入,所以无需重复引入。

3.2 Application

创建 Application.java 类,配置 @SpringBootApplication 注解即可。代码如下:

@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true) // http://www.voidcn.com/article/p-zddcuyii-bpt.html
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}
  • 添加 @EnableAspectJAutoProxy 注解,重点是配置 exposeProxy = true ,因为我们希望 Spring AOP 能将当前代理对象设置到 AopContext 中。具体用途,我们会在下文看到。想要提前看的胖友,可以看看 《Spring AOP 通过获取代理对象实现事务切换》 文章。

先暂时不启动项目。等我们添加好 Controller 。

3.3 UserAddDTO

在 cn.iocoder.springboot.lab22.validation.dto 包路径下,创建 UserAddDTO 类,为用户添加 DTO 类。代码如下:

// UserAddDTO.java

public class UserAddDTO {

    /**
     * 账号
     */
    @NotEmpty(message = "登陆账号不能为空")
    @Length(min = 5, max = 16, message = "账号长度为 5-16 位")
    @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
    private String username;
    /**
     * 密码
     */
    @NotEmpty(message = "密码不能为空")
    @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
    private String password;
    
    // ... 省略 setting/getting 方法
}

每个字段上的约束注解,胖友仔细瞅瞅。

3.4 UserController

在 cn.iocoder.springboot.lab22.validation.controller 包路径下,创建 UserController 类,提供用户 API 接口。代码如下:

// UserController.java

@RestController
@RequestMapping("/users")
@Validated
public class UserController {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @GetMapping("/get")
    public void get(@RequestParam("id") @Min(value = 1L, message = "编号必须大于 0") Integer id) {
        logger.info("[get][id: {}]", id);
    }

    @PostMapping("/add")
    public void add(@Valid UserAddDTO addDTO) {
        logger.info("[add][addDTO: {}]", addDTO);
    }

}
  • 在类上,添加 @Validated 注解,表示 UserController 是所有接口都需要进行参数校验。

  • 对于 #get(id) 方法,我们在 id 参数上,添加了 @Min 注解,校验 id 必须大于 0 。校验不通过示例如下图:

  • 对于 #add(addDTO) 方法,我们在 addDTO 参数上,添加了 @Valid 注解,实现对该参数的校验。校验不通过示例如下图:

    • errors 字段,参数错误明细数组。每一个数组元素,对应一个参数错误明细。这里,username 违背了长度不满足 [5, 16] 。

示例我们是已经成功跑通了,但是呢,这里有几点差异性,我们要来理解下。

艿艿:解释起来,信息量有点大,胖友保持耐心。

也可以不理解,就按照这么使用即可。

第一点,#get(id) 方法上,我们并没有给 id 添加 @Valid 注解,而 #add(addDTO) 方法上,我们给 addDTO 添加 @Valid 注解。这个差异,是为什么呢?

因为 UserController 使用了 @Validated 注解,那么 Spring Validation 就会使用 AOP 进行切面,进行参数校验。而该切面的拦截器,使用的是 MethodValidationInterceptor 。

  • 对于 #get(id) 方法,需要校验的参数 id ,是平铺开的,所以无需添加 @Valid 注解。

  • 对于 #add(addDTO) 方法,需要校验的参数 addDTO ,实际相当于嵌套校验,要校验的参数的都在 addDTO 里面,所以需要添加 @Valid 注解。

第二点,#get(id) 方法的返回的结果是 status = 500 ,而 #add(addDTO) 方法的返回的结果是 status = 400 。

  • 对于 #get(id) 方法,在 MethodValidationInterceptor 拦截器中,校验到参数不正确,会抛出 ConstraintViolationException 异常。

  • 对于 #add(addDTO) 方法,因为 addDTO 是个 POJO 对象,所以会走 SpringMVC 的 DataBinder 机制,它会调用 DataBinder#validate(Object... validationHints) 方法,进行校验。在校验不通过时,会抛出 BindException 。

在 SpringMVC 中,默认使用 DefaultHandlerExceptionResolver 处理异常。

  • 对于 BindException 异常,处理成 400 的状态码。

  • 对于 ConstraintViolationException 异常,没有特殊处理,所以处理成 500 的状态码。

这里,我们在抛个问题,如果 #add(addDTO 方法,如果参数正确,在走完 DataBinder 中的参数校验后,会不会在走一遍 MethodValidationInterceptor 的拦截器呢?思考 100 毫秒...

答案是会。这样,就会导致浪费。所以 Controller 类里,如果只有类似的 #add(addDTO) 方法的嵌套校验,那么我可以不在 Controller 类上添加 @Validated 注解。从而实现,仅使用 DataBinder 中来做参数校验。

第三点,无论是 #get(id) 方法,还是 #add(addDTO) 方法,它们的返回提示都非常不友好,那么该怎么办呢?

参考 《芋道 Spring Boot SpringMVC 入门》 的 「5. 全局异常处理」 ,使用 @ExceptionHandler 注解,实现自定义的异常处理。这个,我们在本文的 4. 处理校验异常 小节中,来提供具体示例。

3.5 UserService

相比在 Controller 添加参数校验来说,在 Service 进行参数校验,会更加安全可靠。艿艿个人建议的话,Controller 的参数校验可以不做,Service 的参数校验一定要做。

在 cn.iocoder.springboot.lab22.validation.service 包路径下,创建 UserService 类,提供用户 Service 逻辑。代码如下:

// UserService.java

@Service
@Validated
public class UserService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    public void get(@Min(value = 1L, message = "编号必须大于 0") Integer id) {
        logger.info("[get][id: {}]", id);
    }

    public void add(@Valid UserAddDTO addDTO) {
        logger.info("[add][addDTO: {}]", addDTO);
    }

    public void add01(UserAddDTO addDTO) {
        this.add(addDTO);
    }

    public void add02(UserAddDTO addDTO) {
        self().add(addDTO);
    }

    private UserService self() {
        return (UserService) AopContext.currentProxy();
    }

}
  • 和 UserController 的方法是一致的,包括注解。

  • 额外添加了 #add01(addDTO) 和 #add02(addDTO) 方法,用于演示方法内部调用。

创建 UserServiceTest 测试类,我们来测试一下简单的 UserService 的每个操作。代码如下:

// UserService.java

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    public void testGet() {
        userService.get(-1);
    }

    @Test
    public void testAdd() {
        UserAddDTO addDTO = new UserAddDTO();
        userService.add(addDTO);
    }

    @Test
    public void testAdd01() {
        UserAddDTO addDTO = new UserAddDTO();
        userService.add01(addDTO);
    }

    @Test
    public void testAdd02() {
        UserAddDTO addDTO = new UserAddDTO();
        userService.add02(addDTO);
    }

}

① #testGet() 测试方法

执行,抛出 ConstraintViolationException 异常。日志如下:

javax.validation.ConstraintViolationException: get.id: 编号必须大于 0

 at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:116)
  • 符合期望。

② #testAdd() 测试方法

执行,抛出 ConstraintViolationException 异常。日志如下:

javax.validation.ConstraintViolationException: add.addDTO.username: 登陆账号不能为空, add.addDTO.password: 密码不能为空

 at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:116)
  • 符合期望。不同于我们在调用 UserController#add(addDTO) 方法,这里被 MethodValidationInterceptor 拦截,进行参数校验,而不是 DataBinder 当中。

③ #testAdd01() 测试方法

执行,正常结束。因为进行 this.add(addDTO) 调用时,this 并不是 Spring AOP 代理对象,所以并不会被 MethodValidationInterceptor 所拦截。

④ #testAdd02() 测试方法

执行,抛出 IllegalStateException 异常。日志如下:

java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.

 at org.springframework.aop.framework.AopContext.currentProxy(AopContext.java:69)
  • 理论来说,因为我们配置了 @EnableAspectJAutoProxy(exposeProxy = true) 注解,在 Spring AOP 拦截时,通过调用 AopContext.currentProxy() 方法,是可以获取到当前的代理对象。结果,此处抛出 IllegalStateException 异常。

  • 显然,这里并没有将当前的代理对象,设置到 AopContext 中,所以抛出 IllegalStateException 异常。目前猜测,可能是 BUG 。???? 暂时木有心情去调试,嘿嘿。

4. 处理校验异常

示例代码对应仓库:lab-22-validation-01 。

在 「3. 快速入门」 中,我们可以看到,如果直接将校验的结果返回给前端,提示内容的可阅读性是比较差的,所以我们需要对校验抛出的异常进行处理。

在 《芋道 Spring Boot SpringMVC 入门》 的 「5. 全局异常处理」 小节中,使用 @ExceptionHandler 注解,实现自定义的异常处理。所以本小节,我们在 「3. 快速入门」 小节的 lab-22-validation-01 示例,进一步处理校验异常。

4.1 复制粘贴

我们先把 《芋道 Spring Boot SpringMVC 入门》 的 「5. 全局异常处理」 小节中,需要用到的类,全部复制过来。

  • 在 cn.iocoder.springboot.lab22.validation.constants 包路径下,复制 ServiceExceptionEnum 类。

  • 在 cn.iocoder.springboot.lab22.validation.core.exception 包路径下,复制 ServiceException 类。

  • 在 cn.iocoder.springboot.lab22.validation.core.vo 包路径下,复制 CommonResult 类。

  • 在 cn.iocoder.springboot.lab22.validation.core.web 包路径下,复制 GlobalExceptionHandler 和 GlobalResponseBodyHandler 类。

4.2 ServiceExceptionEnum

修改 ServiceExceptionEnum 枚举类,增加校验参数不通过的错误码枚举。代码如下:

// ServiceExceptionEnum.java

INVALID_REQUEST_PARAM_ERROR(2001001002, "请求参数不合法"),

4.3 GlobalExceptionHandler

修改 GlobalExceptionHandler 类,增加 #constraintViolationExceptionHandler(...) 方法,处理 ConstraintViolationException 异常。代码如下:

// GlobalExceptionHandler.java

@ResponseBody
@ExceptionHandler(value = ConstraintViolationException.class)
public CommonResult constraintViolationExceptionHandler(HttpServletRequest req, ConstraintViolationException ex) {
    logger.debug("[constraintViolationExceptionHandler]", ex);
    // 拼接错误
    StringBuilder detailMessage = new StringBuilder();
    for (ConstraintViolation<?> constraintViolation : ex.getConstraintViolations()) {
        // 使用 ; 分隔多个错误
        if (detailMessage.length() > 0) {
            detailMessage.append(";");
        }
        // 拼接内容到其中
        detailMessage.append(constraintViolation.getMessage());
    }
    // 包装 CommonResult 结果
    return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(),
            ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage.toString());
}
  • 将每个约束的错误内容提示,拼接起来,使用 ; 分隔。

  • 重新请求 UserController#get(id) 对应的接口,响应结果如下:

修改 GlobalExceptionHandler 类,增加 #bindExceptionHandler(...) 方法,处理 BindException 异常。代码如下:

// GlobalExceptionHandler.java

@ResponseBody
@ExceptionHandler(value = BindException.class)
public CommonResult bindExceptionHandler(HttpServletRequest req, BindException ex) {
    logger.debug("[bindExceptionHandler]", ex);
    // 拼接错误
    StringBuilder detailMessage = new StringBuilder();
    for (ObjectError objectError : ex.getAllErrors()) {
        // 使用 ; 分隔多个错误
        if (detailMessage.length() > 0) {
            detailMessage.append(";");
        }
        // 拼接内容到其中
        detailMessage.append(objectError.getDefaultMessage());
    }
    // 包装 CommonResult 结果
    return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(),
            ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage.toString());
}
  • 将每个约束的错误内容提示,拼接起来,使用 ; 分隔。

  • 重新请求 UserController#add(addDTO) 对应的接口,响应结果如下:

5. 自定义约束

示例代码对应仓库:lab-22-validation-01 。

在大多数项目中,无论是 Bean Validation 定义的约束,还是 Hibernate Validator 附加的约束,都是无法满足我们复杂的业务场景。所以,我们需要自定义约束。

开发自定义约束一共只要两步:1)编写自定义约束的注解;2)编写自定义的校验器 ConstraintValidator 。

下面,就让我们一起来实现一个自定义约束,用于校验参数必须在枚举值的范围内。

5.1 IntArrayValuable

在 cn.iocoder.springboot.lab22.validation.core.validator 包路径下,创建 IntArrayValuable 接口,用于返回值数组。代码如下:

// IntArrayValuable.java

public interface IntArrayValuable {

    /**
     * @return int 数组
     */
    int[] array();

}

因为对于一个枚举类来说,我们无法获得它具体有那些值。所以,我们会要求这个枚举类实现该接口,返回它拥有的所有枚举值。

5.2 GenderEnum

在 cn.iocoder.springboot.lab22.validation.constants 包路径下,创建 GenderEnum 枚举类,枚举性别。代码如下:

// GenderEnum.java

public enum GenderEnum implements IntArrayValuable {

    MALE(1, "男"),
    FEMALE(2, "女");

    /**
     * 值数组
     */
    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(GenderEnum::getValue).toArray();

    /**
     * 性别值
     */
    private final Integer value;
    /**
     * 性别名
     */
    private final String name;

    GenderEnum(Integer value, String name) {
        this.value = value;
        this.name = name;
    }

    public Integer getValue() {
        return value;
    }

    public String getName() {
        return name;
    }

    @Override
    public int[] array() {
        return ARRAYS;
    }

}
  • 实现 IntArrayValuable 接口,返回值数组 ARRAYS 。

5.3 @InEnum

在 cn.iocoder.springboot.lab22.validation.core.validator 包路径下,创建 @InEnum 自定义约束的注解。代码如下:

// InEnum.java

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = InEnumValidator.class)
public @interface InEnum {

    /**
     * @return 实现 IntArrayValuable 接口的
     */
    Class<? extends IntArrayValuable> value();

    /**
     * @return 提示内容
     */
    String message() default "必须在指定范围 {value}";

    /**
     * @return 分组
     */
    Class<?>[] groups() default {};

    /**
     * @return Payload 数组
     */
    Class<? extends Payload>[] payload() default {};

    /**
     *  Defines several {@code @InEnum} constraints on the same element.
     */
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List {

        InEnum[] value();

    }

}
  • 在类上,添加 @@Constraint(validatedBy = InEnumValidator.class) 注解,设置使用的自定义约束的校验器。

  • value() 属性,设置实现 IntArrayValuable 接口的类。这样,我们就能获得参数需要校验的值数组。

  • message() 属性,设置提示内容。默认为 "必须在指定范围 {value}" 。

  • 其它属性,复制粘贴即可,都可以忽略不用理解。

5.4 InEnumValidator

在 cn.iocoder.springboot.lab22.validation.core.validator 包路径下,创建 InEnumValidator 自定义约束的校验器。代码如下:

// InEnumValidator.java

public class InEnumValidator implements ConstraintValidator<InEnum, Integer> {

    /**
     * 值数组
     */
    private Set<Integer> values;

    @Override
    public void initialize(InEnum annotation) {
        IntArrayValuable[] values = annotation.value().getEnumConstants();
        if (values.length == 0) {
            this.values = Collections.emptySet();
        } else {
            this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toSet());
        }
    }

    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        // <2.1> 校验通过
        if (values.contains(value)) {
            return true;
        }
        // <2.2.1>校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
        context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
        context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
                .replaceAll("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句      
        return false; // <2.2.2.>
    }

}
  • 实现 ConstraintValidator 接口。

    • 第一个泛型为 A extends Annotation ,设置对应的自定义约束的注解。例如说,这里我们设置了 @InEnum 注解。

    • 第二个泛型为 T ,设置对应的参数值的类型。例如说,这里我们设置了 Integer 类型。

  • 实现 #initialize(annotation) 方法,获得 @InEnum 注解的 values() 属性,获得值数组,设置到 values 属性种。

  • 实现 #isValid(value, context) 方法,实现校验参数值,是否在 values 范围内。

    • <2.1> 处,校验参数值在范围内,直接返回 true ,校验通过。

    • <2.2.1> 处,校验不通过,自定义提示语句。

    • <2.2.2> 处,校验不通过,所以返回 false 。

至此,我们已经完成了自定义约束的实现。下面,我们来进行下测试。

5.5 UserUpdateGenderDTO

在 cn.iocoder.springboot.lab22.validation.dto 包路径下,创建 UserUpdateGenderDTO 类,为用户更新性别 DTO。代码如下:

// UserUpdateGenderDTO.java

public class UserUpdateGenderDTO {

    /**
     * 用户编号
     */
    @NotNull(message = "用户编号不能为空")
    private Integer id;

    /**
     * 性别
     */
    @NotNull(message = "性别不能为空")
    @InEnum(value = GenderEnum.class, message = "性别必须是 {value}")
    private Integer gender;
    
    // ... 省略 set/get 方法
}
  • 在 gender 字段上,添加 @InEnum(value = GenderEnum.class, message = "性别必须是 {value}") 注解,限制传入的参数值,必须在 GenderEnum 枚举范围内。

5.6 UserController

修改 UserController 类,增加修改性别 API 接口。代码如下:

// UserController.java

@PostMapping("/update_gender")
public void updateGender(@Valid UserUpdateGenderDTO updateGenderDTO) {
    logger.info("[updateGender][updateGenderDTO: {}]", updateGenderDTO);
}

模拟请求该 API 接口,响应结果如下:

因为我们传入的请求参数 gender 的值为 null ,显然不在 GenderEnum 范围内,所以校验不通过,输出 "性别必须是 [1, 2]" 。

6. 分组校验

示例代码对应仓库:lab-22-validation-01 。

在一些业务场景下,我们需要使用分组校验,即相同的 Bean 对象,根据校验分组,使用不同的校验规则。咳咳咳,貌似我们暂时没有这方面的诉求。即使有,也是拆分不同的 Bean 类。当然,作为一篇入门的文章,艿艿还是提供下分组校验的示例。

6.1 UserUpdateStatusDTO

在 cn.iocoder.springboot.lab22.validation.dto 包路径下,创建 UserUpdateStatusDTO 类,为用户更新状态 DTO 。代码如下:

// UserUpdateStatusDTO.java

public class UserUpdateStatusDTO {

    /**
     * 分组 01 ,要求状态必须为 true
     */
    public interface Group01 {}

    /**
     * 状态 02 ,要求状态必须为 false
     */
    public interface Group02 {}
    
    /**
     * 状态
     */
    @AssertTrue(message = "状态必须为 true", groups = Group01.class)
    @AssertFalse(message = "状态必须为 false", groups = Group02.class)
    private Boolean status;

    // ... 省略 set/get 方法
}
  • 创建了 Group01 和 Group02 接口,作为两个校验分组。不一定要定义在 UserUpdateStatusDTO 类中,这里仅仅是为了方便。

  • status 字段,在 Group01 校验分组时,必须为 true ;在 Group02 校验分组时,必须为 false 。

6.2 UserController

修改 UserController 类,增加两个修改状态的 API 接口。代码如下:

// UserController.java

@PostMapping("/update_status_true")
public void updateStatusTrue(@Validated(UserUpdateStatusDTO.Group01.class) UserUpdateStatusDTO updateStatusDTO) {
    logger.info("[updateStatusTrue][updateStatusDTO: {}]", updateStatusDTO);
}

@PostMapping("/update_status_false")
public void updateStatusFalse(@Validated(UserUpdateStatusDTO.Group02.class) UserUpdateStatusDTO updateStatusDTO) {
    logger.info("[updateStatusFalse][updateStatusDTO: {}]", updateStatusDTO);
}
  • 对于 #updateStatusTrue(updateStatusDTO) 方法,我们在 updateStatusDTO 参数上,添加了 @Validated 注解,并且设置校验分组为 Group01 。校验不通过示例如下图:

  • 对于 #updateStatusFalse(updateStatusDTO) 方法,我们在 updateStatusDTO 参数上,添加了 @Validated 注解,并且设置校验分组为 Group02 。校验不通过示例如下图:

所以,使用分组校验,核心在于添加上 @Validated 注解,并设置对应的校验分组。

7. 手动校验

示例代码对应仓库:lab-22-validation-01 。

在上面的示例中,我们使用的主要是 Spring Validation 的声明式注解。然而在少数业务场景下,我们可能需要手动使用 Bean Validation API ,进行参数校验。

修改 UserServiceTest 测试类,增加手动参数校验的示例。代码如下:

// UserServiceTest.java

@Autowired // <1.1>
private Validator validator;

@Test
public void testValidator() {
    // 打印,查看 validator 的类型 // <1.2>
    System.out.println(validator);

    // 创建 UserAddDTO 对象 // <2>
    UserAddDTO addDTO = new UserAddDTO();
    // 校验 // <3>
    Set<ConstraintViolation<UserAddDTO>> result = validator.validate(addDTO);
    // 打印校验结果 // <4>
    for (ConstraintViolation<UserAddDTO> constraintViolation : result) {
        // 属性:消息
        System.out.println(constraintViolation.getPropertyPath() + ":" + constraintViolation.getMessage());
    }
}
  • <1.1> 处,注入 Validator Bean 对象。

  • <1.2> 处,打印 validator 的类型。输出如下:

    org.springframework.validation.beanvalidation.LocalValidatorFactoryBean@48c3205a
    
    • validator 的类型为 LocalValidatorFactoryBean 。LocalValidatorFactoryBean 提供 JSR-303、JSR-349 的支持,同时兼容 Hibernate Validator 。

    • 在 Spring Boot 体系中,使用 ValidationAutoConfiguration 自动化配置类,默认创建 LocalValidatorFactoryBean 作为 Validator Bean 。

  • <2> 处,创建 UserAddDTO 对象,即 「3.3 UserAddDTO」 ,已经添加相应的约束注解。

  • <3> 处,调用 Validator#validate(T object, Class<?>... groups) 方法,进行参数校验。

  • <4> 处,打印校验结果。输出如下:

    username:登陆账号不能为空
    password:密码不能为空
    
    • 如果校验通过,则返回的 Set<ConstraintViolation<?>> 集合为空。

8. 国际化 i18n

示例代码对应仓库:lab-22-validation-01 。

在一些项目中,我们会有国际化的需求,特别是我们在做 TOB 的 SASS 化服务的时候。那么,显然我们在使用 Bean Validator 做参数校验的时候,也需要提供国际化的错误提示。

给力的是,Hibernate Validator 已经内置了国际化的支持,所以我们只需要简单的配置,就可以实现国际化的错误提示。

8.1 应用配置文件

在 resources 目录下,创建 application.yaml 配置文件。配置如下:

spring:
  # i18 message 配置,对应 MessageSourceProperties 配置类
  messages:
    basename: i18n/messages # 文件路径基础名
    encoding: UTF-8 # 使用 UTF-8 编码

然后,我们在 resources/i18 目录下,创建不同语言的 messages 文件。如下:

  • messages.properties :默认的 i18 配置文件。

    UserUpdateDTO.id.NotNull=用户编号不能为空
    
  • messages_en.properties :英文的 i18 配置文件。

    UserUpdateDTO.id.NotNull=userId cannot be empty
    
  • messages_ja.properties :日文的 i18 配置文件。

    UserUpdateDTO.id.NotNull=ユーザー番号は空にできません
    

8.2 ValidationConfiguration

在 cn.iocoder.springboot.lab22.validation.config 包路径下,创建 ValidationConfiguration 配置类,用于创建一个支持 i18 国际化的 Validator Bean 对象。代码如下:

// ValidationConfiguration.java

@Configuration
public class ValidationConfiguration {

    /**
     * 参考 {@link ValidationAutoConfiguration#defaultValidator()} 方法,构建 Validator Bean
     *
     * @return Validator 对象
     */
    @Bean
    public Validator validator(MessageSource messageSource)  {
        // 创建 LocalValidatorFactoryBean 对象
        LocalValidatorFactoryBean validator = ValidationAutoConfiguration.defaultValidator();
        // 设置 messageSource 属性,实现 i18 国际化
        validator.setValidationMessageSource(messageSource);
        // 返回
        return validator;
    }

}

8.3 UserUpdateDTO

在 cn.iocoder.springboot.lab22.validation.dto 包路径下,创建 UserUpdateDTO 类,为用户更新 DTO 。代码如下:

// UserUpdateDTO.java

public class UserUpdateDTO {

    /**
     * 用户编号
     */
    @NotNull(message = "{UserUpdateDTO.id.NotNull}")
    private Integer id;

    // ... 省略 get/set 方法
    
}
  • 不同于我们上面看到的约束注解的 message 属性的设置,这里我们使用了 {} 占位符。

8.4 UserController

修改 UserController 类,增加用户更新的 API 接口。代码如下:

// UserController.java

@PostMapping("/update")
public void update(@Valid UserUpdateDTO updateDTO) {
    logger.info("[update][updateDTO: {}]", updateDTO);
}

下面,我们来进行下 API 接口测试。有一点要注意,SpringMVC 通过 Accept-Language 请求头,实现 i18n 国际化。

  • Accept-Language = zh 的情况,响应结果如下:

  • Accept-Language = en 的情况,响应结果如下:

  • Accept-Language = ja 的情况,响应结果如下:

至此,我们的 Validator 的 i18n 国际化已经完成了。

不过细心的胖友,会发现 "请求参数不合法" 并没有国际化处理。是的~实际上,国际化是个大工程,涉及到方方面面。例如说,业务信息表的国际化,商品同时支持中文、英文、韩文等多种语言。???? 最近艿艿手头有个新项目,需要做国际化,有这方面需求的胖友,可以一起多多交流呀。

666. 彩蛋

希望阅读完本文,能够让胖友更加舒适且优雅的完成各种需要参数校验的地方。???? 不说了,艿艿赶紧给自己的系统去把参数校验给补全,嘿嘿。

当然,有一点要注意,Bean Validation 更多做的是,无状态的参数校验。怎么理解呢?

  • 例如说,参数的大小长度等等,是适合通过 Bean Validation 中完成。

  • 例如说,校验用户名唯一等等,依赖外部数据源的,是不适合通过 Bean Validation 中完成。

当然,如果胖友有不同意见,欢迎留言讨论。

受限于篇幅,艿艿偷懒了下,还有一些内容其实可以补充:

  • 《Intro to Apache BVal》 使用 Apache BVal 实现参数校验。

  • 《使用 Spring 的 Validator 接口进行校验》 ,通过实现 Validator 接口,提供对应 Bean 的参数校验器。



欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢:

已在知识星球更新源码解析如下:

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 20 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

SpringBoot 使用 Validation 进行参数校验并统一返回校验异常 新建自定义注解java复制代码import com.jk.validator.StatusValidator;String message() default "状态传递有误";Class 阅读详情

相关推荐

Spring Boot 参数校验 Validation 使用

1. 基于 Bean Validation 标准:Spring Validation 是基于 Bean Validation 标准的参数校验框架,可以实现对 Java Bean 属性和方法参数的校验,提供了一套完整的校验规范和 API,可以很方便地进行扩展和定制。 2. 支持多种校验注解:Spring Validation 支持多种校验注解,比如 @NotNull、@Size、@Min、@Max 等,可以满足不同的校验需求,同时也支持自定义校验注解。 3. 集成方便:Spring Validation 是

ITKidKid的博客 1628

controller中获取heard中参数方法

在controller层获得参数方法具体实现如下: 获得当前HttpServletRequest对象 protected HttpServletRequest getRequest(){ return ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); } 将hearder中的参数放到map中。 protected Map<String..

沛哥儿的专栏 3761

Spring Boot 实现各种参数校验

业务需求总是比框架提供的这些简单校验要复杂的多,我们可以自定义校验来满足我们的需求。自定义约束注解String message() default "加密id格式错误";Class

weixin_50667675的博客 1524

validation--数据校验

validation,数据校验

qq_15210269的博客 3930

【java】[validation校验]----《Spring Boot 3 整合 Validation:注解使用、全局异常处理与自定义校验》

本文介绍了在Spring Boot 3中整合Validation框架实现参数校验的方法。主要内容包括:1)添加spring-boot-starter-validation依赖;2)在实体类字段上使用Jakarta EE 9+的校验注解(如@NotBlank、@Size等);3)通过全局异常处理器统一处理MethodArgumentNotValidException和ConstraintViolationException两种校验异常;4)提供统一的错误响应格式。文章还给出了测试示例,展示了如何验证参数校验功

记录生活 9330

SpringBoot+@Validate+全局异常拦截实现自定义规则参数校验(校验get请求参数不能为空且在指定枚举类型中)

上面实现SpringBoot参数校验时以自带的注解进行校验,如果需要进行自定义校验规则,比如请求时必须携带某个请求码,而且该请求码字符串必须在指定范围,即从枚举类中指定包含的。上面是以post请求为例,下面以get请求为例。

BADAO_LIUMANG_QIZHI的博客 4569

快速入手 Spring Boot 参数校验

点击上方“芋道源码”,选择“设为星标”做积极的人,而不是积极废人!源码精品专栏原创 | Java 2019 超神之路,很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网络...

芋艿V 139

An AI compute of cities based on Distributed-Platform and Distributed-Databases(天池地铁流量预测)

摘要 在日益城市化的今天,城市居民出行问题逐渐成为衡量一个城市现代化能力的标准;城市出行方式多元化的同时,也对各交通系统的调度运营能力提出了更高的要求:如何提高运营效率,如何运用有限的安保力量进行有效的安检部署等问题一直在阻挠着城市交通的发展。 An AI compute of cities based on Distributed-Platform and Distributed-Databas...

Clearlove灬Star 5164

【spring】参数校验Validation

在实际开发中,我们无法保证客户端传来的请求都是合法的。比如一些要求必传的参数没有传递,传来的参数长度不符合要求等,这种时候如果放任不管,继续执行后续业务逻辑,很有可能就会出现意想不到的bug。有人可能会说,这不是前端的问题吗,让前端校验去。话是这么说,但我们也不能前端校验百分百不会出现问题。并且有些请求可能也不是正规通过客户端发来的,可能是黑客恶意攻击,又或是通过Postman等发来的,这些请求就不一定会“合法”了。因此,对客户端传来的每个请求都进行必要的参数校验是十分重要的。

acm_pn的博客 5078

SoC设计中的RDC陷阱:如何避免异步复位导致的亚稳态问题

本文深入探讨了SoC设计中由异步复位引发的复位域交叉(RDC)陷阱及其导致的亚稳态风险。通过分析实际案例,文章系统性地提出了以同步复位优先、异步复位同步释放为核心的设计原则,并介绍了电路级加固与验证策略,为构建稳健的SoC复位架构提供了实用指南。

weixin_29280657的博客 422

深度解读:车辆诊断之DTC的状态位

搞车辆诊断测试的,离不开对DTC的状态位的测试DTC的状态位是8个bit,每个bit代表不同的意思,每个bit置1或者置0都要满足一定的条件初学者,很容易被这个搞得头很大今天我们就来详细拆解一下,扫除你心中的疑惑我们以欠压故障为例进行说明,假设欠压故障(DTC code为EE0468)发生的条件如下:1. 车辆模式(CarModSts1)处于Normal(0)或者Dyno(5)2. 当电源可用性不受限制时,即ElPowerLevel (有和PwrLvlElecSubtyp组成。

帮助100万人颠覆负面潜意识,实现人生蜕变,培养1万名潜意识开发导师 1万+

Spring Boot 参数校验 Validation 入门

文章摘要:本文介绍了在Spring Boot中使用Hibernate Validator进行参数校验的方法。概述了参数校验的重要性,列举了Bean Validation和Hibernate Validator提供的常用校验注解,并对比了@Valid和@Validated注解的区别。文章包含快速入门指南,展示如何通过引入依赖、配置Application类来实现对Controller和Service层的参数校验。核心要点包括:1)参数校验是保证API可靠性的必要环节;2)通过注解方式实现声明式校验;3)@Val

无忧无虑的Coding~~~ 1216

人形机器人触觉革命:压电式电子皮肤的多模态感知与自修复技术突破

本文深入探讨了压电式电子皮肤如何推动人形机器人的触觉革命。文章分析了其基于压电效应的多模态感知原理,能够灵敏检测压力、振动与温度,并重点介绍了赋予电子皮肤自修复能力的动态共价网络等前沿材料技术,从而极大提升了机器人与物理世界进行精细、安全交互的可靠性与智能化水平。

weixin_29251567的博客 195

Spring Boot + validation + AOP 请求参数校验

Spring Boot + validation + AOP 请求参数校验 一、validation 校验注解 通用 @Null 被注释的属性必须为 null @NotNull被注释的属性必须不为 null @AssertTrue 被注释的属性必须为 true @AssertFalse 被注释的属性必须为 false 字符串/数组/集合检查:(字符串本身就是个数组) @Pattern(reg...

浓密秀发之谦先生的博客 1788

ICV:Metal Fill Insertion流程

如果不想删除先前加的metal fill,可以使用signoff_create_metal_fill -mode add命令,当使用这个mode后,工具不会fix density问题,哪怕设置了signoff.create_metal_fill.fix_density_errors,设置这个mode后可以使用以下option控制metal fill 的插入。默认情况下,signoff_create_metal_fill是会删掉已经加进去的metal/via fill再重新加的。

m0_61544122的博客 1134

npm install failed如何办?

上网搜索,说是网络配置不太对。按照如下几步,保证解决问题。在运行npm install指令时,出现如下错误。

xiezhipu的博客 880

Spring Boot 参数校验 Validation 终极指南

注解使用场景抛出异常@Valid校验对象参数(如 @RequestBody)@Validated校验简单类型参数(如 @RequestParam)@Validated校验对象参数(需配合 @Valid 使用)在大多数项目中,无论是 Bean Validation 定义的约束,还是 Hibernate Validator 附加的约束,都是无法满足我们复杂的业务场景。所以,我们需要自定义约束。1)编写自定义约束的注解;2)编写自定义的校验器 ConstraintValidator。

〆、挽风的博客 2183

PostgreSQL的学习心得和知识总结(八十二)|深入理解PostgreSQL数据库SQL语句的词法解析和语法解析流程

postgresql 词法解析语法解析流程

孤傲小二~阿沐的博客 5450

spring-boot-starter-validation进行参数校验

引入依赖pom.xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> 常用注解 注意不用错了类型! 注解 限制 @Null 限制只能为

吹灭读书灯 一身都是月的博客 8371

麒麟操作系统提示“默认密钥环已上锁”的解决办法

麒麟操作系统提示“默认密钥环已上锁”的解决办法

太极淘的博客 1万+

贝叶斯统计 [韦来生编]

贝叶斯(Bayes)统计是近几十年来迅速发展起来的数理统计的一个重要分支。贝叶斯方法与经典统计方法的主要不同之处是进行统计推断时除了利用样本信息外,还要利用参数的先验信息,因此可以提高统计推断或统计决策的效果。它在经济、金融、生物、医学、自然科学和社会科学等许多领域具有广泛的应用。贝叶斯方法的研究已渗透到了统计学的几乎所有领域。作者在给中国科学技术大学概率论与数理统计专业本科生开设的“数理统计”课程中有一章专门讲授“贝叶斯方法和统计决策理论”,近十年来也给中国科学技术大学概率统计专业研究生开设过几次“贝叶斯分析”课。本书是在对过去讲稿的内容作了适当的增减和调整的基础上完成的。  本书共分六章。第1章是绪论,介绍了贝叶斯统计的若干基本概念,同时对必要的数理统计的基础知识有重点地作了回顾。第二章介绍了确定先验分布的若干可供选择的方法。第三章介绍了几类常见统计模型参数的后验分布的主要结果和计算方法。第四章和第五章分别介绍了“贝叶斯统计推断”和“贝叶斯统计决策”的内容。第六章介绍了贝叶斯统计计算的若干方法,包括蒙特卡洛方法和马尔可夫链蒙特卡洛(MCMC)方法以及相关统计软件的简介。本书的每一章附右大量的例题和习题,书末附有常用的几个表格和部分习题解答供读者查用。  大约可在54学时内讲授本书的第1章至第六章的主要内容,教师可根据学时需要选讲本书第1章至第六章的部分内容。书中标“*”号的章节可略去不讲,留给读者作为阅读材料。第六章例题中的R代码和部分例题的数据文件等可从作者个人主页下载。  作者在编写本书的过程中参考了J.O.Berger教授的《统计决策论及贝叶斯分析》和茆诗松教授编写的《贝叶斯统计》等书中的一些内容,以及《贝叶斯分析》一书中由张伟平博士编写的有关贝叶斯统计计算的一些内容。在此表示衷心感谢。  本书编写准备过程中,中国科学技术大学统计与金融系的几个研究生帮助完成了本书部分中文的录入和排版,作者对他们的辛勤工作表示真诚的感谢。高等教育出版社为本书的出版给予了大力支持。在此一并致谢。

SpringBoot的validation参数校验

采用 validation来对参数进行校验简化代码。

qq_42211536的博客 3772

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Spring Boot 实现接口的各种参数校验

互联网大厂比较喜欢的人才特点:对技术有热情,强硬的技术基础实力;主动,善于团队协作,善于总结思考。无论是哪家公司,都很重视高并发高可用技术,重视基础,所以千万别小看任何知识。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。其实我写了这么多,只是我自己的总结,并不一定适用于所有人,相信经过一些面试,大家都会有这些感触。下面有部分截图希望能对大家有所帮助。

2401_85112041的博客 2463

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

使用spring-boot-starter-validation做参数校验

使用spring-boot-starter-validation做参数校验添加依赖新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图...

日有寸进 1万+

spring-boot-starter-validation是什么&Validation参数校验使用概要

Valid 是 JSR303 声明的,JSR是Java Specification Requests的缩写,其中 JSR303 是JAVA EE 6 中的一项子规范,叫做 Bean Validation,为 JavaBean 验证定义了相应的元数据模型和 API,需要注意的是,JSR 只是一项标准,它规定了一些校验注解的规范,但没有实现,二者主要作用在于 都作为标准JSR-303规范,在检验Controller的入参是否符合规范时,使用@Validated或者@Valid在基本验证功能上没有太多区别。

A-Itfuture的博客 2998

【SpringBoot框架篇】26.集成spring-boot-starter-validation对接口参数校验

文章目录1.常规验证2.使用validation验证2.1.添加依赖2.2.常用注解2.3.在pojo类中添加需要使用的注解2.4.在接口入参处添加@Valid2.5.添加全局异常处理类返回校验错位提示信息 1.常规验证 以下面的登录接口为例,需要手动去验证参数是否存在,这种写法繁琐且不太优雅。 @RestController public class LoginController { @RequestMapping("/login") public String test(@Reque

皓亮君的博客 2590

Spring Boot 参数校验 Validation 从入门到精通

本文详细介绍了SpringBoot参数校验的全面知识体系,主要内容包括:1.参数校验基础概念与重要性,2.SpringBoot集成校验框架的配置方法,3.核心校验注解分类详解(空值、布尔值、数值、字符串等),4.高级特性如分组校验、级联校验和集合校验,5.自定义校验注解的实现方式,6.全局异常处理与统一返回结果封装,7.实战应用场景(RESTful API、业务对象、文件上传),8.高级特性(国际化、条件校验)与性能优化建议,9.测试验证方法,10.常见问题解决方案。文章系统性地讲解了如何利用SpringB

油墨香^-^的博客 977
上一篇: 不要网上乱拷贝代码了!一段网上找的代码突然炸了!
下一篇: Elasticsearch 在各大互联网公司大量真实的应用案例!
公众号-芋道源码
博客等级 码龄10年 1636粉丝 377原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值