SpringBoot参数校验底层原理和实操。深度历险、深度解析(图解+秒懂+史上最全)

简介: SpringBoot参数校验底层原理和实操。深度历险、深度解析(图解+秒懂+史上最全)

本文 的 原文 地址

原始的内容,请参考 本文 的 原文 地址

本文 的 原文 地址

本文作者:

  • 第一作者 老架构师 肖恩(肖恩 是尼恩团队 高级架构师,负责写此文的第一稿,初稿 )
  • 第二作者 老架构师 尼恩 (45岁老架构师, 负责 提升此文的 技术高度,让大家有一种 俯视 技术、俯瞰技术、 技术自由 的感觉

参数校验简介

什么是SpringBoot 参数校验?

在实际开发中,拿到数据后的第一步通常是检查数据是否正确

如果只是简单的格式错误(比如字段为空、数字超出范围),我们可以通过注解来快速判断并提示用户。

但有些校验涉及业务逻辑,比如“购买金额 = 单价 × 数量”,这种就不能靠简单注解完成,需要更灵活的方式处理。

怎么灵活的处理参数校验? 可以用 Spring 提供的 Validator 接口 来实现复杂规则校验。

不过,硬编码写校验逻辑太麻烦了。

现在主流做法是使用 JSR 303 标准,它是一套 Java 提供的数据校验规范,支持通过注解方式直接写在类属性上,比如:


@NotNull String name;
@Min(18) int age;

Spring 从 3.0 版本开始支持 JSR 303,不同版本支持的功能略有不同:

Spring 版本 支持标准 验证框架版本 主要功能
Spring 3.0 JSR 303 Hibernate Validator 4 基础注解校验
Spring 4.0 JSR 349 Hibernate Validator 5 分组校验、方法校验
Spring 5.0+ JSR 380 Hibernate Validator 6 完整支持 Bean Validation 2.0

常用注解一览表

注解 作用 示例
@NotNull 不为 null @NotNull String name
@NotBlank 字符串非空且不能全是空格 @NotBlank String password
@Min / @Max 数值最小/最大限制 @Min(18) int age
@Email 检查邮箱格式 @Email String email
@Pattern 正则匹配 @Pattern(regexp="^[a-zA-Z0-9]+$")
@Future 必须是未来时间 @Future LocalDate expireDate

更多注解可以参考官方文档或 IDE 自动提示。

JSR 303 实现版本:Hibernate Validator

Hibernate Validator 是 JSR 303 的一个具体实现。

除了支持所有标准注解外,Hibernate Validator 还扩展了一些实用注解,比如:

注解 说明
@URL 检查是否是合法 URL
@Length 字符串长度范围限制
@Range 数值或字符串必须在指定范围内

总结一句话:

使用 JSR 303 + Hibernate Validator 可以让我们在 Spring Boot 中轻松实现数据合法性校验,减少手动判断代码,提高开发效率

参数校验实操

第一步:引入依赖

Spring Boot 提供了一个模块叫:spring-boot-starter-validation,它已经集成了 Hibernate Validator,你只需要引入依赖就可以直接使用。


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

这样就能在 Controller 或 DTO 中使用注解进行自动校验了。

第二步:定义一个 全局异常处理器 GlobalExceptionHandler

这是一个统一处理程序错误的地方,叫做全局异常处理器。

它的作用是:当程序出错时,自动捕获这些错误,并返回友好的提示信息,而不是直接崩溃。


@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
    //参数校验异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public Result error(MethodArgumentNotValidException e){
        log.warn(e.getMessage());
        return Result.fail()
                .code(ResultCodeEnum.ARGUMENT_VALID_ERROR.getCode())
                .message(e.getBindingResult().getFieldError().getDefaultMessage());
    }
    //参数校验异常
    @ExceptionHandler(BindException.class)
    @ResponseBody
    public Result error(BindException e){
        log.warn(e.getMessage());
        StringBuilder sb = new StringBuilder();
        for (ObjectError error : e.getBindingResult().getAllErrors()) {
            sb.append(error.getDefaultMessage());
        }
        return Result.fail()
                .code(ResultCodeEnum.ARGUMENT_VALID_ERROR.getCode())
                .message(sb.toString());
    }
    //参数校验异常
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public Result error(ConstraintViolationException e){
        log.warn(e.getMessage());
        StringBuilder sb = new StringBuilder();
        for (ConstraintViolation<?> violation : e.getConstraintViolations()) {
            sb.append(violation.getMessage());
        }
        return Result.fail()
                .code(ResultCodeEnum.ARGUMENT_VALID_ERROR.getCode())
                .message(sb.toString());
    }

    //全局异常
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result error(Exception e){
        log.warn(e.getMessage());
        return Result.fail().message("执行了全局异常处理");
    }
}

下面是它处理几种常见错误的方式:

(1) 参数验证错误(MethodArgumentNotValidException):

当用户提交的数据格式不对时(比如注册时少填了手机号),会返回具体哪个字段错了。

(2) 绑定参数错误(BindException):

这也是参数问题的一种,但可能涉及多个字段出错。这个方法会把所有错误信息拼在一起返回。

(3) 违反约束规则的错误(ConstraintViolationException):

比如某个字段必须大于0,但用户输入了-1。这时也会收集所有错误提示并返回。

(4) 其他所有未处理的异常(Exception):

如果发生了没特别处理的错误,就走这个兜底的方法,返回一个通用错误提示。

第三步:定义实体类

下面是两个 Java 类,用于接收用户新增和修改时传入的数据。

它们分别定义了创建用户和更新用户时需要填写的字段,并通过注解对字段做了基本校验。

(1)创建用户(UserCreateVO)

这个类用于创建新用户,包含以下字段:

  • 用户名:不能为空
  • 姓名:不能为空
  • 手机号:必须是11位数字
  • 性别:不能为空(用整数表示,比如 0 表示女,1 表示男)

@Data
public class UserCreateVO {
   

    @NotBlank(message = "用户名不能为空")
    private String userName;

    @NotBlank(message = "姓名不能为空")
    private String name;

    @Size(min=11,max=11,message = "手机号长度不符合要求")
    private String phone;

    @NotNull(message = "性别不能为空")
    private Integer sex;
}

(2)更新用户(UserUpdateVO)

这个类用于更新已有用户信息,相比创建用户,多了一个 id 字段,用来指定要更新的是哪个用户。

  • id:不能为空,表示用户的唯一标识
  • 其他字段与创建用户一致

@Data
public class UserUpdateVO {
   

    @NotBlank(message = "id不能为空")
    private String id;

    @NotBlank(message = "用户名不能为空")
    private String userName;

    @NotBlank(message = "姓名不能为空")
    private String name;

    @Size(min=11,max=11,message = "手机号长度不符合要求")
    private String phone;

    @NotNull(message = "性别不能为空")
    private Integer sex;
}

第四步:定义请求接口

这是一个用户管理的接口类,主要处理用户的创建和更新操作

  • 创建用户:/user/create
  • 更新用户:/user/update

代码如下:


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


    @PostMapping("create")
    public Result createUser(@Validated @RequestBody UserCreateVO userCreateVo){
        return Result.success("参数校验成功");
    }

    @PostMapping("update")
    public Result updateUser(@Validated @RequestBody UserUpdateVO userUpdateVo){
        return Result.success("参数校验成功");
    }

}

第五步:校验测试

新增接口测试

下面这个请求,是故意没有填写用户名的,用来测试接口在缺少参数时的反应。

image-20250627201528448

create接口的POST 操作



POST http://localhost:8080/user/create
Content-Type: application/json

{
  "userName": "",
  "name": "sean",
  "phone": "12345678901",
  "sex": 1
}

系统会返回一个响应结果,截图如下:

image-20250627202146412

更新接口测试

接下来可以测试更新用户信息的接口,具体操作类似上面的流程,只是接口和参数可能会有变化。

update 接口的POST 操作



POST http://localhost:8080/user/update
Content-Type: application/json

{
  "id": "",
  "userName": "sean",
  "name": "sean",
  "phone": "12345678901",
  "sex": 1
}

返回结果截图如下:

image-20250627202101878

第六步:路径传参校验

有时候我们会通过 URL 地址传参数,比如根据 ID 获取用户信息。

这时候我们可以用 @PathVariable 来接收参数,并加上校验规则。

示例代码如下:


    @GetMapping("getUserById/{id}")
    public Result getUserById(@PathVariable @Size(min=2,max=5,message = "id长度不符合要求") String id){
        return Result.success("参数校验成功");
    }

测试说明:

  • 当我们访问类似 /getUserById/ab 的地址时,因为 id 是 "ab",符合长度要求,所以能正常返回“校验通过”。

  • 如果我们访问 /getUserById/a,长度不够,就会返回提示:“id长度要在2到5个字符之间”。

    getUserById 的get 请求参考demo:


GET http://localhost:8080/user/getUserById/1234567890

结果:

image-20250627202419640

可以看到,校验规则也是生效的。

第七步:分组校验

我们前边写了两个 VO 类:UserCreateVO 和 UserUpdateVO。

但是开发时一般都是一个VO, 一个VO如何实现不同方法,有不同的校验规则呢?

比如我们新增的时候一般不需要 id,但是修改的时候需要传入 id。

分组校验接口

在做数据校验时,有时候需要根据不同的操作(比如新增或修改)来使用不同的校验规则。

我们可以通过定义“分组”来实现这一点。

下面是一个简单的例子:


public class UserGroup {
   

    // 定义一个用于创建用户的校验分组
    public interface CreateGroup extends Default {
   
    }

    // 定义一个用于更新用户的校验分组
    public interface UpdateGroup extends Default {
   
    }
}

这样,在实际校验时就可以指定使用哪一组规则,比如新增用户时用 CreateGroup,修改用户信息时用 UpdateGroup

统一VO校验类

这是一个用户信息的 Java 类 UserVo,用于接收前端传来的用户数据。

它定义了以下几个字段和校验规则:

  • id:用户唯一标识,不能为空,仅在更新用户时使用。
  • userName:用户名,不能为空,新增和更新都要校验。
  • name:真实姓名,不能为空,新增和更新都要校验。
  • phone:手机号,必须是合法格式,新增和更新都要校验。
  • sex:性别,不能为空,新增和更新都要校验。

@Data
public class UserVo {

    @NotBlank(message = "id不能为空",groups = UserGroup.UpdateGroup.class)
    private String id;

    @NotBlank(message = "用户名不能为空",groups =
            {UserGroup.CreateGroup.class,UserGroup.UpdateGroup.class})
    private String userName;

    @NotBlank(message = "姓名不能为空",groups =
            {UserGroup.CreateGroup.class,UserGroup.UpdateGroup.class})
    private String name;

//    @Size(min=11,max=11,message = "手机号长度不符合要求",groups =
//            {UserGroup.CreateGroup.class,UserGroup.UpdateGroup.class})
    @PhoneValid(message = "请填写正确的手机号", groups =
            {UserGroup.CreateGroup.class,UserGroup.UpdateGroup.class})
    private String phone;

    @NotNull(message = "性别不能为空",groups =
            {UserGroup.CreateGroup.class,UserGroup.UpdateGroup.class})
    private Integer sex;

}

修改接口

注意:分组校验不能使用@Valid , 需要换 一个 注解

参数上边添加 @Validated 接口,并指定分组名称,并统一使用 UserVO 类接收参数。


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


    @PostMapping("create")
    public Result createUser(@Validated(UserGroup.CreateGroup.class) @RequestBody UserVo userVO) {
        return Result.success("参数校验成功");
    }

    @PostMapping("update")
    public Result updateUser(@Validated(UserGroup.UpdateGroup.class) @RequestBody UserVo userVO) {
        return Result.success("参数校验成功");
    }


    @GetMapping("getUserById/{id}")
    public Result getUserById(@PathVariable @Size(min=2,max=5,message = "id长度不符合要求") String id){
        return Result.success("参数校验成功");
    }

}
  • 使用 @Validated 实现分组校验,不能用 @Valid
  • 所有参数统一封装到 UserVO
  • 校验失败会自动返回错误信息,成功则继续执行
  • URL 路径参数也可以加校验规则,比如长度限制等

第八步:嵌套对象校验

存在嵌套对象校验的时候,使用 @Valid 注解解决。

添加一个角色实体

这是一个用来保存角色信息的类,主要包含角色名称。


@Data
public class Role {
    @NotBlank(message = "角色名称不能为空")
    private String roleName;
}

修改VO类

在 User 类中引入角色实体,并加入 @Valid 注解。


@Data
public class UserVo {

    @NotBlank(message = "id不能为空",groups = UserGroup.UpdateGroup.class)
    private String id;

    @NotBlank(message = "用户名不能为空",groups =
            {UserGroup.CreateGroup.class,UserGroup.UpdateGroup.class})
    private String userName;

    @NotBlank(message = "姓名不能为空",groups =
            {UserGroup.CreateGroup.class,UserGroup.UpdateGroup.class})
    private String name;

//    @Size(min=11,max=11,message = "手机号长度不符合要求",groups =
//            {UserGroup.CreateGroup.class,UserGroup.UpdateGroup.class})
    @PhoneValid(message = "请填写正确的手机号", groups =
            {UserGroup.CreateGroup.class,UserGroup.UpdateGroup.class})
    private String phone;

    @NotNull(message = "性别不能为空",groups =
            {UserGroup.CreateGroup.class,UserGroup.UpdateGroup.class})
    private Integer sex;

    @Valid
    @NotNull(message = "角色信息不能为空")
    private Role role;
}

校验测试

测试新增接口 create 接口,故意不传角色信息:



POST http://localhost:8080/user/create
Content-Type: application/json

{
  "userName": "xxx",
  "name": "sean",
  "phone": "13345678901",
  "sex": 1,
  "role": {
    "roleName": ""
  }
}

结果:

image-20250627204933473

修改测试也一样结果

两大注解的对比:@Valid@Validated对比

一般Controller 层,可以用 @Valid 校验请求数据格式

一般Service 层,用 @Validated 校验业务规则

从原理上解释这两个注解

  • @Valid的触发时机是在HandlerMethodArgumentResolver这个组件封装解析参数是触发

  • @Validated如果在Service层中使用,是AOP机制,通过MethodValidationInterceptorAOP代理在方法调用是触发

  • @Validated如果只是在Controller层方法中使用,Spring MVC 会将其视为 @Valid 的增强版,仍通过 RequestResponseBodyMethodProcessor 触发校验

通过一个表个对比这两个注解

对比维度 @Valid (JSR-380 标准) @Validated (Spring 扩展)
来源 Java 官方标准(javax.validation Spring 框架扩展(org.springframework
作用目标 字段、方法参数、嵌套对象 类、方法、参数
主要用途 标记需要校验的对象 触发校验流程(支持分组和方法级校验)
触发机制 通过 RequestResponseBodyMethodProcessor 在数据绑定时触发 通过 MethodValidationInterceptor (AOP代理) 在方法调用时触发
校验阶段 参数绑定阶段 方法调用阶段
分组校验 不支持 支持(通过 groups 属性)
嵌套对象校验 需要显式标注 @Valid 自动级联校验(无需 @Valid
方法级校验 仅支持参数校验 支持方法参数和返回值校验
校验触发时机 数据绑定时(如接收 HTTP 请求) 方法调用时(通过 Spring AOP 代理)
异常类型 MethodArgumentNotValidException ConstraintViolationException
典型应用场景 Controller 层校验请求数据 Service 层校验业务参数和返回值
代码示例 @PostMapping void save(@Valid @RequestBody User user) @Validated class Service { void update(@Min(1) Long id) }
  • @Valid:是"校验标签",贴在数据上声明需要检查什么(What to validate)。
  • @Validated:是"校验开关",控制如何检查和扩展功能(How to validate)。

  • 嵌套对象:混合使用(@Validated 类 + @Valid 字段)

快速失败配置

默认情况下,参数校验会把所有规则都检查一遍,等全部检查完才告诉你哪里错了。

这种方式效率不高,尤其在有很多规则的时候。

我们可以通过开启“快速失败”模式来优化:一旦发现错误,就立刻停止后续检查。

快速失败配置 方式如下:


/**
 * 参数校验相关配置
 */
@Configuration
public class ValidConfig {
    /**
     * 快速返回校验器
     * @return
     */
    @Bean
    public Validator validator(){
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                .configure()
                //快速失败模式
                .failFast(true)
                .buildValidatorFactory();
        return validatorFactory.getValidator();
    }

    /**
     * 设置快速校验,返回方法校验处理器
     * 使用MethodValidationPostProcessor注入后,会启动自定义校验器
     * @return
     */
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor(){
        MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
        methodValidationPostProcessor.setValidator(validator());
        return methodValidationPostProcessor;
    }
}

自定义校验规则

有时候,框架自带的数据检查功能不够用,我们就得自己写规则来验证数据。

下面以手机号验证为例,教你怎么自定义一个验证注解。

定义注解


@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Constraint(validatedBy = PhoneValidator.class)
public @interface PhoneValid {
    String message() default "请填写正确的手机号";

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


    Class<? extends Payload>[ ] payload() default {};

}

@Constraint 注解是 Hibernate Validator 的注解,用于实现自定义校验规则,通过 validatedBy 参数指定进行参数校验的实现类。

校验实现类

我们要实现一个手机号的校验功能,需要创建一个类 PhoneValidator,让它实现 Java 的 ConstraintValidator 接口。


public class PhoneValidator implements ConstraintValidator<PhoneValid,Object> {
    /**
     * 11位手机号的正则表达式,以13、14、15、17、18头
     * ^:匹配字符串的开头
     * 13\d:匹配以13开头的手机号码
     * 14[579]:匹配以145、147、149开头的手机号
     * 15[^4\D]:匹配以15开头且第3位数字不为4的手机号码
     * 17[^49\D]:匹配以17开头且第3位数字部位4或9的手机号码
     * 18\d :匹配以18开头的手机号码
     * \d{8}:匹配手机号码的后8位,即剩余的8个数字
     * $:匹配字符串的结尾
     */
    public static final String REGEX_PHONE="^(13\\d|14[579]|15[^4\\D]|17[^49\\D]|18\\d)\\d{8}$";


    //初始化注解
    @Override
    public void initialize(PhoneValid constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
    }

    //校验参数,true表示校验通过,false表示校验失败
    @Override
    public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
        String phone = String.valueOf(o);
        if(phone.length()!=11){
            return false;
        }
        //正则校验
        return phone.matches(REGEX_PHONE);
    }
}

在 ConstraintValidator 中,第一个参数为注解,即 Annotation,第二个参数是泛型。

这个正则表达式用来判断手机号是否合法:

  • 必须是11位数字
  • 开头只能是:13、145/147/149、15开头但第三位不能是4、17开头但第三位不能是4或9、18
  • 后面8位可以是任意数字

在实体中添加自定义注解

image-20250627205616715

测试自定义校验

调用 create 接口,根据自定义校验正则,手机号必须13、14、15、17、18开头



POST http://localhost:8080/user/create
Content-Type: application/json

{
  "userName": "xxx",
  "name": "sean",
  "phone": "12345678901",
  "sex": 1,
  "role": {
    "roleName": ""
  }
}

结果:

image-20250627205815944

Service层 参数校验 的介绍和实操

上面实操都是讲的控制层(Controller)的校验,Spring Boot参数校验 在Service也可以使用。

Service层主要校验业务方法参数/返回值,确保业务规则合规,如金额范围、状态合法性等。

支持分组校验、方法级校验、嵌套对象自动级联(需@Valid),需配合全局异常处理返回友好错误。

Service层的参数,通过@Validated触发AOP代理,利用Hibernate Validator执行JSR-380校验,基于注解(如@NotNull@Min)定义规则,失败抛出ConstraintViolationException

下面介绍下基本使用方法

(1) UserService


@Service
@Validated  // 必须添加此注解
public class UserService {

    public String createUser(@Valid UserCreateVO vo) {
        return "service 校验通过";
    }

}

(2) UserUpdateVO


@Data
public class UserUpdateVO {

    @NotBlank(message = "id不能为空")
    private String id;

    @NotBlank(message = "用户名不能为空")
    private String userName;

    @NotBlank(message = "姓名不能为空")
    private String name;

    @Size(min=11,max=11,message = "手机号长度不符合要求")
    private String phone;

    @NotNull(message = "性别不能为空")
    private Integer sex;
}

(3) 测试


@SpringBootTest
class UserServiceTest {

    @Resource
    UserService userService;

    @Test
    void createUser() {
        UserCreateVO vo = new UserCreateVO();
//        vo.setUserName("");
        vo.setName("sean");
        vo.setPhone("12345678901");
        vo.setSex(18);
        System.out.println(userService.createUser(vo));
    }
}

执行结果

因为userName为空,校验失败

image-20250626205521570

刨根问底:Controller 层校验的实现原理

Spring Boot 在 Controller 层做参数校验,是通过 @Valid 注解来触发的。

它遵循 JSR-380 标准,底层由 MethodValidationPostProcessorLocalValidatorFactoryBean 来完成。

整个校验流程如下:

(1) 请求进来时,参数会被绑定到一个对象上。

(2) Spring 会用 Hibernate Validator 检查这个对象字段上的约束注解(比如 @NotNull@Size 等)。

(3) 校验结果会保存在 BindingResult 中。

(4) 如果校验失败,就会抛出 MethodArgumentNotValidException 异常。

(5) 最终,这个异常会被 ExceptionHandler 捕获,并返回友好的错误信息给前端。

这套机制和 Spring MVC 的参数解析流程紧密结合,使用起来非常方便。

一、整体架构图

(1) 前端请求到达 DispatcherServlet

这是 Spring MVC 的入口,负责协调整个请求流程。

(2) 调用 RequestMappingHandlerAdapter 执行方法 :

它负责找到对应的方法来处理请求。

(3) 使用 RequestResponseBodyMethodProcessor 解析参数 :

它会检查是否有需要校验的参数,并进行处理。

(4) 创建 WebDataBinder 来绑定和校验数据 :

它会根据配置决定是否启用参数校验。

(5) 从 LocalValidatorFactoryBean 获取校验器 :

这个类负责生成一个真正的校验工具。

(6) 最终由 HibernateValidator 执行参数校验 :

它是实际做数据校验的工具,比如判断字段是否为空、长度是否合适等。

二、启动初始化流程

Spring Boot 在启动时会自动配置数据校验功能。

这个过程主要涉及几个关键步骤:

(1) 加载自动配置:

Spring Boot 启动时,会加载校验相关的自动配置类 ValidationAutoConfiguration

(2) 创建校验工厂 Bean:

该配置类会创建一个 LocalValidatorFactoryBean 实例。

(3) 初始化校验器:

这个工厂 Bean 会去初始化 Hibernate 提供的校验器 HibernateValidator

(4) 注册为 Bean:

最终,这个校验器会被注册成 Spring 容器中的一个 Bean,供其他组件使用。

关键源码:

这段代码的作用就是创建了一个 LocalValidatorFactoryBean 对象,并把它交给 Spring 管理。

它内部会负责加载和管理 HibernateValidator


// ValidationAutoConfiguration.java
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public LocalValidatorFactoryBean defaultValidator() {
   
    LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
    // 配置消息插值器等
    return factoryBean;
}

三、请求处理全流程

当我们通过浏览器或其他客户端发送一个请求给服务器时,Spring MVC 框架会按照一定的流程来处理这个请求,尤其是对传入的参数进行校验。

整个流程可以简单理解为以下几个步骤:

(1) 接收到请求:服务器接收到 HTTP 请求。

(2) 找到对应的处理方法:框架根据请求路径找到要执行的 Controller 方法。

(3) 准备处理参数:创建一个“数据绑定器”来把请求中的数据转换成 Java 对象。

(4) 获取校验规则:如果参数需要校验(比如不能为空、长度限制等),就准备好对应的校验器。

(5) 开始校验参数:如果参数没问题,继续执行 Controller 中的方法,并返回 200 成功响应。如果参数有问题,停止执行,直接返回 400 错误和错误信息。

整个流程就是:接收请求 → 找到方法 → 准备参数 → 校验参数 → 执行方法或报错返回

这是一套标准的 Spring MVC 参数校验机制,帮助我们确保传入的数据是合法的。

四、核心源码深度解析

1. 参数解析入口(RequestResponseBodyMethodProcessor)

这个方法的作用是处理控制器中带有 @RequestBody 注解的方法参数。

它的主要任务是:读取请求数据、转换成 Java 对象、做参数校验、返回结果

核心流程如下:

(1) 读取请求体并转为对象

从请求中拿到原始数据(比如 JSON),然后根据参数类型转成对应的 Java 对象。

(2) 创建数据绑定器

创建一个数据绑定器(WebDataBinder),用于后续的参数校验。

(3) 执行参数校验

如果参数上有校验注解(如 @Valid),就进行校验。

(4) 检查是否有错误

如果校验失败,就抛出异常,阻止方法继续执行。

源码如下:


// RequestResponseBodyMethodProcessor.java
public Object resolveArgument(MethodParameter parameter, 
                            ModelAndViewContainer mavContainer,
                            NativeWebRequest webRequest,
                            WebDataBinderFactory binderFactory) throws Exception {
   

    // 1. 读取请求体并转换对象
    Object arg = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType());

    // 2. 创建数据绑定器(关键点)
    WebDataBinder binder = binderFactory.createBinder(
        webRequest, arg, getValidationHints(parameter));

    // 3. 执行校验(核心逻辑)
    validateIfApplicable(binder, parameter);

    // 4. 检查校验结果
    if (binder.getBindingResult().hasErrors()) {
   
        throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
    }

    return arg;
}

2. 校验触发逻辑(validateIfApplicable)

在处理请求参数时,Spring 会检查方法参数上有没有校验相关的注解,比如 @Valid@Validated

如果有的话,就会触发参数的校验流程。

这个过程主要分为两个步骤:

(1) 查找注解:

从方法参数中找出所有注解。

(2) 判断是否需要校验:

如果是 @Valid 或以 Valid 开头的注解,就按默认规则校验。如果是 @Validated 注解,还可以指定分组校验,根据分组信息来决定校验哪些字段。

一旦确认要校验,就会调用 binder.validate() 方法开始执行校验逻辑。


protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
   
    Annotation[] annotations = parameter.getParameterAnnotations();
    for (Annotation ann : annotations) {
   
        // 处理@Valid和@Validated注解
        Object[] validationHints = determineValidationHints(ann);
        if (validationHints != null) {
   
            binder.validate(validationHints); // 触发校验
            break;
        }
    }
}

// 识别校验注解
protected Object[] determineValidationHints(Annotation ann) {
   
    Class<? extends Annotation> annotationType = ann.annotationType();
    if ("javax.validation.Valid".equals(annotationType.getName()) ||
        annotationType.getSimpleName().startsWith("Valid")) {
   
        return new Object[0]; // 返回空数组表示默认分组
    }
    // 处理@Validated的分组逻辑
    if (ann instanceof Validated validatedAnn) {
   
        return validatedAnn.value();
    }
    return null;
}

3. 校验执行过程(DataBinder)

当你调用 WebDataBinder.validate 方法时,整个校验过程会按以下步骤进行:

(1) 找到要校验的数据对象

先获取你传进来的目标对象(比如一个表单提交过来的 Java 对象)。

(2) 拿到对应的校验工具

从配置中取出一个合适的校验器(Validator),准备开始校验。

(3) 执行校验操作

使用这个校验器对目标对象进行检查,比如判断字段是否为空、长度是否符合要求等。

(4) 收集校验结果

把校验过程中发现的问题记录下来,比如哪些字段有问题、错误信息是什么。

(5) 把结果存起来

最后把这些错误信息保存到 BindingResult 中,供后续使用,比如返回给前端展示。

下面这段代码是实际执行校验的地方:


// DataBinder.java
public void validate(Object... validationHints) {
   
    Object target = getTarget();
    if (target != null) {
   
        // 获取校验器并执行校验
        getValidator().validate(target, 
            getValidationHints(validationHints)); // 处理分组
    }
}

这段代码的意思是:如果目标对象存在,就用当前的校验器去校验它,并根据传入的参数决定是否按特定分组来校验。

4. Hibernate Validator 执行细节

下面是一个对象校验方法的简化说明。它用来检查一个对象的数据是否符合设定的规则。


// ValidatorImpl.java
public <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
   
    // 1. 获取Bean的元数据(缓存机制)
    BeanMetaData<T> rootBeanMetaData = beanMetaDataManager.getBeanMetaData(object.getClass());

    // 2. 创建校验上下文
    ValidationContext<T> validationContext = getValidationContextBuilder()
        .forValidate(object, rootBeanMetaData);

    // 3. 执行实际校验
    validateConstraintsForGroups(validationContext, groups);

    // 4. 返回校验结果
    return validationContext.getFailingConstraints();
}

这个方法的作用是验证某个对象是否满足我们定义的规则(比如不能为空、必须是数字等)。流程如下:

(1) 获取对象的结构信息:

先看看这个对象长什么样,有哪些字段需要校验(用的是缓存,提高效率)。

(2) 准备校验环境:

把对象和它的结构信息放在一起,准备好开始校验。

(3) 执行校验:

根据指定的规则组,检查对象的每个字段有没有问题。

(4) 返回错误信息:

如果有不合规的地方,就返回这些错误;没有的话就返回空集合。

五、异常处理机制

1. 异常转换流程

在处理请求时,如果数据有问题,系统会按以下流程返回错误信息:

(1) 验证数据:

系统先检查传入的数据是否符合要求。

(2) 记录错误:

如果有问题,会记录具体的错误内容。

(3) 抛出异常:

系统识别到错误后,会抛出一个“参数不合法”的异常。

(4) 返回错误:

最后,系统会把错误信息以 400 错误码的形式返回给调用方。

2. 默认异常处理(ResponseEntityExceptionHandler)

这是一个处理请求参数错误的方法。当用户提交的数据不符合要求时,这个方法会生成一个结构化的错误信息返回给用户。

主要流程如下:

(1) 捕获异常:

当出现参数校验失败的错误(MethodArgumentNotValidException)时,进入这个处理方法。

(2) 构造错误响应体:

创建一个基础错误对象 ProblemDetail,包含错误状态、提示信息等。

(3) 提取具体错误信息:

从异常中取出所有字段错误信息,格式化为“字段名: 错误原因”。

(4) 返回错误响应:

调用统一异常处理方法,将错误信息返回给客户端。


// ResponseEntityExceptionHandler.java
@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<Object> handleMethodArgumentNotValid(
        MethodArgumentNotValidException ex, HttpHeaders headers, 
        HttpStatus status, WebRequest request) {
   

    // 构造错误响应体
    ProblemDetail body = createProblemDetail(ex, status, 
                                           "Invalid request content", 
                                           null, null, request);

    // 转换错误信息
    body.setProperty("errors", ex.getBindingResult().getAllErrors().stream()
        .map(error -> {
   
            if (error instanceof FieldError fieldError) {
   
                return fieldError.getField() + ": " + fieldError.getDefaultMessage();
            }
            return error.getDefaultMessage();
        })
        .collect(Collectors.toList()));

    return handleExceptionInternal(ex, body, headers, status, request);
}

六、校验注解处理流程

当系统需要处理一个请求时,会经历以下几个关键步骤来进行数据校验:

(1) 数据状态确认:

判断当前数据是否是有效的或已验证的。

(2) 处理器识别:

RequestResponseBodyMethodProcessor 识别并准备处理这个请求的数据。

(3) 创建绑定器:

生成一个 WebDataBinder,用于后续的数据绑定和校验。

(4) 获取校验规则:

从配置中取出对应的 Validator 校验器。

(5) 解析约束条件:

分析字段上的注解规则(比如 @NotNull、@Size 等)。

(6) 执行校验:

对数据按照规则进行检查。

(7) 判断结果:

如果通过,继续后续流程;

如果失败,进入错误处理流程。

(8) 收集错误信息:

把不符合规则的地方记录下来。

(9) 转换为字段错误:

将错误信息转成标准的 FieldError 格式。

(10) 保存到结果对象:

最终把这些错误存入 `BindingResult`,供程序使用。

刨根问底:Service 层校验的实现原理

Spring Boot Service层校验通过@Validated触发,基于AOP代理实现:

(1) 代理机制:

MethodValidationPostProcessor@Validated类创建代理,拦截方法调用;

(2) 校验时机:

MethodValidationInterceptor在方法执行前校验参数,执行后校验返回值;

(3) 异常处理:

失败时抛出ConstraintViolationException,需全局捕获;

(4) 嵌套校验:

自动级联(无需@Valid),依赖Hibernate Validator执行JSR-380规则。

一、整体架构图

整个流程主要涉及几个关键步骤:

(1) 初始化校验器(Validator)

Spring 会通过 LocalValidatorFactoryBean 创建一个校验工具 ValidatorImpl,它负责具体的校验工作。

(2) 创建拦截器(Interceptor)

MethodValidationPostProcessor 会创建一个拦截器 MethodValidationInterceptor,它的作用是在方法执行前后做参数和返回值的检查。

(3) 代理目标服务类(ServiceImpl)

被 `@Validated` 注解标记的服务类会被代理,这样在调用其方法时,就能触发参数校验逻辑。

(4) 执行校验逻辑

拦截器在调用方法前,使用 ValidatorImpl 对参数进行校验;方法执行后,还会校验返回值是否符合要求。

二、启动初始化流程

Spring 在启动时会准备两个关键组件:MethodValidationPostProcessorLocalValidatorFactoryBean

  • LocalValidatorFactoryBean 会去创建一个真正的校验工具 HibernateValidator
  • MethodValidationPostProcessor 会拿到这个校验工具,并创建一个 AOP 切面,用来拦截带有 @Validated 注解的类,做参数校验。

关键源码:


// MethodValidationPostProcessor.java
public void afterPropertiesSet() {
   
    // 创建切入点(匹配@Validated注解的类)
    Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true);

    // 创建通知(校验拦截器)
    this.advisor = new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice(this.validator));
}

protected Advice createMethodValidationAdvice(Validator validator) {
   
    return new MethodValidationInterceptor(validator);
}

总结一句话:Spring 启动时准备好校验工具,并通过 AOP 对需要校验的方法进行拦截处理。

三、方法调用校验流程

流程说明:

(1) 用户调用服务方法

用户通过代理对象调用一个服务方法。

(2) 方法被拦截器拦截

拦截器(MethodValidationInterceptor)会先介入处理。

(3) 参数校验开始

拦截器调用校验器(ValidatorImpl)来检查传入的参数是否合法。

(4) 校验结果判断

如果参数没问题:继续执行真正的服务方法,并返回结果。如果参数有问题:直接抛出异常,告诉用户哪里不对。

(5) 返回值也做校验(可选)

有些时候还会对方法返回的结果进行一次检查,确保输出也符合要求。

这个流程的核心就是:调用 → 拦截 → 参数检查 → 成功就执行,失败就报错。

四、核心源码深度解析

1. 校验拦截器实现

下面是一个拦截器方法的逻辑,用于在调用方法前后进行参数和返回值的校验。


// MethodValidationInterceptor.java
public Object invoke(MethodInvocation invocation) throws Throwable {
   
    // 1. 参数校验
    Set<ConstraintViolation<Object>> violations = this.validator.forExecutables()
        .validateParameters(
            invocation.getThis(),
            invocation.getMethod(),
            invocation.getArguments());

    if (!violations.isEmpty()) {
   
        throw new ConstraintViolationException(violations);
    }

    // 2. 执行方法
    Object returnValue = invocation.proceed();

    // 3. 返回值校验
    violations = this.validator.forExecutables()
        .validateReturnValue(
            invocation.getThis(),
            invocation.getMethod(),
            returnValue);

    if (!violations.isEmpty()) {
   
        throw new ConstraintViolationException(violations);
    }

    return returnValue;
}

这个方法主要做了三件事:

(1) 参数校验:检查传给方法的参数是否符合规范。

(2) 执行方法:如果参数没问题,就正常执行方法。

(3) 返回值校验:检查方法返回的结果是否符合要求。

流程图:

2. 校验执行细节(Hibernate Validator)

在调用某个方法之前,检查它的参数是否符合设定的规则。比如某个参数不能为 null,或者必须是正数等。

基本流程:

(1) 获取这个方法有哪些参数需要校验

(2) 依次检查每个参数

  • 拿到当前参数的值

  • 构造一个上下文环境,用来记录当前校验的状态

  • 根据规则对这个参数进行校验

(3) 最后返回所有不符合规则的地方(如果有)

流程图:


// ValidatorImpl.java
public <T> Set<ConstraintViolation<T>> validateParameters(T object, Method method, 
    Object[] parameterValues, Class<?>... groups) {
   

    // 获取方法约束元数据
    MethodConstraintMappingContext<T> methodContext = getConstraintsForMethod(method);

    // 遍历每个参数
    for (int i = 0; i < parameterValues.length; i++) {
   
        Object value = parameterValues[i];
        ValueContext<T, Object> valueContext = ValueContext.getLocalExecutionContext(
            validatorScopedContext, object, value, i);

        // 执行校验
        validateConstraintsForCurrentGroup(valueContext, 
            methodContext.getParameterConstraints(i), 
            groups);
    }

    return violations;
}

3. 约束验证过程

基本步骤:

(1) 获取参数值:拿到要验证的数据。

(2) 查找约束注解:看看这个数据上有没有设置校验规则。

(3) 创建校验上下文:准备一个环境,用来做后续的校验工作。

(4) 匹配约束验证器:找到能处理这条规则的“检查员”。

(5) 执行验证逻辑:让“检查员”开始检查数据是否符合规则。

(6) 判断是否通过验证:如果通过,继续检查下一条规则。如果不通过,记录问题。

(7) 收集到结果集:把所有发现的问题汇总起来,返回给用户。

五、分组校验实现原理

当你调用校验方法,并指定只校验某个分组(比如 MyGroup.class)时,系统会按照以下流程处理:

(1) 发起校验请求:你告诉校验器(Validator)要校验哪些分组。

(2) 查找约束规则:校验器去查看所有规则(Constraint),找出哪些规则属于你指定的分组。

(3) 判断是否需要校验:对每条规则,校验器判断它是否匹配你传入的分组。如果匹配,就执行这条规则的校验。

(4) 返回结果:校验完成后,把结果返回给你。

关键源码:


// ConstraintDescriptorImpl.java
public Set<Class<?>> getGroups() {
   
    return this.annotationDescriptor.getGroups();
}

// ValidatorImpl.java
protected boolean isValidationRequired(ConstraintDescriptor<?> descriptor, 
                                     Class<?>[] groups) {
   
    // 检查约束分组是否匹配
    return groups.length == 0 || 
           !Collections.disjoint(Arrays.asList(groups), descriptor.getGroups());
}

六、异常处理机制

1. 异常转换流程

在程序中,当数据校验出错时,处理流程如下:

(1) 校验器(Validator) 先检查输入的数据有没有问题,并把所有发现的问题整理成一个列表(ConstraintViolation集合)。

(2) 拦截器(MethodValidationInterceptor) 接收到这些问题后,会抛出一个异常(ConstraintViolationException),告诉系统哪里出错了。

(3) 异常处理器(ExceptionHandler) 捕获到这个异常后,会把它转换成一个友好的错误信息。

(4) 最后,这个错误信息会被返回给调用接口的客户端(Client)。

整个过程是为了让调用者能清楚地知道输入哪里不对,方便他们调整数据。

2. 自定义异常处理示例

基本工作流程如下:

(1) 拦截所有 ConstraintViolationException 类型的异常(通常是参数校验失败引发的)

(2) 从异常中提取出每个字段的错误信息

(3) 如果有字段路径,就把路径和错误信息一起整理出来

(4) 最后统一返回 HTTP 状态码 400 和错误详情

源码如下:


@RestControllerAdvice
public class ServiceValidationHandler {
   

    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<ErrorResponse> handleConstraintViolation(ConstraintViolationException ex) {
   
        List<String> errors = ex.getConstraintViolations().stream()
            .map(v -> {
   
                String path = v.getPropertyPath().toString();
                return StringUtils.hasText(path) ? path + ": " + v.getMessage() : v.getMessage();
            })
            .collect(Collectors.toList());

        return ResponseEntity.badRequest()
            .body(new ErrorResponse("VALIDATION_FAILED", errors));
    }
}

七、与Controller层校验的差异

特性 Service 层校验 Controller 层校验
激活方式 类级别@Validated 参数级别@Valid/@Validated
实现机制 基于AOP代理 基于参数解析器
校验目标 方法参数和返回值 主要针对请求体
异常类型 ConstraintViolationException MethodArgumentNotValidException
分组校验 必须显式指定 支持默认分组
返回值校验 支持 不支持
  • Controller 层校验:适合在接收前端请求时,对传入的数据做初步检查。
  • Service 层校验:用于业务逻辑中更严格的参数和结果验证,适用于复杂场景。
  • 两者都依赖同一个验证机制(如 Hibernate Validator),只是使用方式和适用范围不同。

超底层原理:SpringMVC参数解析机制

HandlerMethodArgumentResolver 是 Spring MVC 中处理控制器方法参数的核心接口,它定义了如何将 HTTP 请求中的各种数据解析为控制器方法的参数。

下面我将从多个维度详细解析其工作原理。

一、核心架构图

在 Spring MVC 中,处理请求参数的核心流程是这样的:

当浏览器发来一个请求时,Spring 需要根据请求里的数据,给控制器方法的参数赋值。这个工作是由一组叫 参数解析器 的组件完成的。

这些参数解析器都实现了同一个接口:HandlerMethodArgumentResolver,它有两个主要任务:

(1) supportsParameter:

判断自己能不能处理某个参数。

(2) resolveArgument:

真正去获取参数的值。

常见的几种参数解析器包括:

  • RequestResponseBodyMethodProcessor:用来处理加了 @RequestBody 注解的参数,从请求体中读取数据。
  • RequestParamMethodArgumentResolver:处理加了 @RequestParam 注解的参数,从请求参数中提取值。
  • ModelAttributeMethodProcessor:处理加了 @ModelAttribute 注解的参数,把请求数据封装成对象。

image-20250705131449417

二、工作流程

1. 整体处理流程

基本流程:

(1) 前端控制器(DispatcherServlet)

收到请求后,会调用处理器适配器(HandlerAdapter)来处理这个请求。

(2) 处理器适配器

会把方法需要的参数交给参数解析器(ArgumentResolvers)去处理。

(3) 参数解析器会逐个检查每个参数:

先判断自己能不能解析这个参数(supportsParameter)。

如果可以,就去解析它的值(resolveArgument)。

(4) 所有参数解析完成后,返回一个参数值数组给处理器适配器。

(5) 最后,完成请求处理

处理器适配器通过反射调用控制器(Controller)里的具体方法,完成请求处理。

2. 解析器链工作流程

基本流程:

(1) 开始

流程启动。

(2) 获取第一个解析器’:

尝试使用第一个解析器来处理参数。

(3) 判断是否支持该参数?

  • 如果支持,就调用 resolveArgument 来获取参数值,然后返回结果。

  • 如果不支持,就去获取下一个解析器,重复这个判断过程。

(4) 循环查找解析器:

直到找到能处理该参数的解析器为止。

三、核心源码解析

1. 接口定义

用于处理控制器方法中的参数解析。它有两个核心方法:

(1) supportsParameter

看看这个参数能不能被当前的解析器处理。

(2) resolveArgument

从请求中提取出参数的具体值,比如从 URL、Body 或者 Session 中取数据。


public interface HandlerMethodArgumentResolver {
   
    /**
     * 判断是否支持该参数
     */
    boolean supportsParameter(MethodParameter parameter);

    /**
     * 解析参数值
     */
    Object resolveArgument(MethodParameter parameter, 
                         ModelAndViewContainer mavContainer,
                         NativeWebRequest webRequest,
                         WebDataBinderFactory binderFactory) throws Exception;
}

2. 典型实现示例(RequestParam)

这个类的作用是处理控制器方法中的参数,特别是那些带有 @RequestParam 注解的参数。

主要流程:

  • 判断是否支持该参数 检查参数是否有 @RequestParam 注解,或者是不是基本类型(如 String、int 等)。如果是,就由这个类来处理。

  • 解析参数值 根据参数名从请求中取出对应的值。

  • 设置默认值和类型转换 如果取不到值但有默认值,就用默认值。最后把值转成参数需要的类型。


// RequestParamMethodArgumentResolver.java
public boolean supportsParameter(MethodParameter parameter) {
   
    // 检查是否有@RequestParam注解或简单类型参数
    return parameter.hasParameterAnnotation(RequestParam.class) ||
           (this.useDefaultResolution && 
            BeanUtils.isSimpleProperty(parameter.getParameterType()));
}

public Object resolveArgument(MethodParameter parameter, ...) throws Exception {
   
    // 获取请求参数名
    String name = determineName(parameter);

    // 从请求中获取值
    Object arg = resolveName(name, parameter, webRequest);

    // 类型转换
    if (arg == null && defaultValue != null) {
   
        arg = defaultValue;
    }
    return convertIfNecessary(name, arg, parameter);
}

3. 参数解析入口(RequestMappingHandlerAdapter)

篇幅太长,请参见原文

本文 的 原文 地址

原始的内容,请参考 本文 的 原文 地址

本文 的 原文 地址原文

相关文章
|
28天前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
|
3月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
759 0
|
4月前
|
JavaScript 前端开发 Java
垃圾分类管理系统基于 Spring Boot Vue 3 微服务架构实操指南
本文介绍了基于Java技术的垃圾分类管理系统开发方案与实施案例。系统采用前后端分离架构,后端使用Spring Boot框架搭配MySQL数据库,前端可选择Vue.js或Java Swing实现。核心功能模块包括垃圾分类查询、科普教育、回收预约等。文中提供了两个典型应用案例:彭湖花园小区使用的Swing桌面系统和基于Spring Boot+Vue的城市管理系统,分别满足不同场景需求。最新技术方案升级为微服务架构,整合Spring Cloud、Redis、Elasticsearch等技术,并采用Docker容器
223 0
|
21天前
|
Java 数据库 数据安全/隐私保护
Spring Boot四层架构深度解析
本文详解Spring Boot四层架构(Controller-Service-DAO-Database)的核心思想与实战应用,涵盖职责划分、代码结构、依赖注入、事务管理及常见问题解决方案,助力构建高内聚、低耦合的企业级应用。
319 0
|
3月前
|
机器学习/深度学习 XML Java
【spring boot logback】日志logback格式解析
在 Spring Boot 中,Logback 是默认的日志框架,它支持灵活的日志格式配置。通过配置 logback.xml 文件,可以定义日志的输出格式、日志级别、日志文件路径等。
510 5
|
3月前
|
Java 关系型数据库 数据库连接
Spring Boot项目集成MyBatis Plus操作PostgreSQL全解析
集成 Spring Boot、PostgreSQL 和 MyBatis Plus 的步骤与 MyBatis 类似,只不过在 MyBatis Plus 中提供了更多的便利功能,如自动生成 SQL、分页查询、Wrapper 查询等。
285 3
|
3月前
|
Java 数据库连接 API
Java 8 + 特性及 Spring Boot 与 Hibernate 等最新技术的实操内容详解
本内容涵盖Java 8+核心语法、Spring Boot与Hibernate实操,按考试考点分类整理,含技术详解与代码示例,助力掌握最新Java技术与应用。
112 2
|
3月前
|
前端开发 Java API
酒店管理系统基于 JavaFX Spring Boot 和 React 经典项目重构实操
本文介绍了基于现代技术栈的酒店管理系统开发方案,整合了JavaFX、Spring Boot和React三大技术框架。系统采用前后端分离架构,JavaFX构建桌面客户端,React开发Web管理界面,Spring Boot提供RESTful API后端服务。核心功能模块包括客房管理和客户预订流程,文中提供了JavaFX实现的客房管理界面代码示例和React开发的预订组件代码,展示了如何实现客房信息展示、添加修改操作以及在线预订功能。
199 1
|
Java Maven 容器
SpringBoot 核心原理分析
SpringBoot 核心原理分析
257 0
SpringBoot 核心原理分析