Skip to main content

数据绑定概述

Spring MVC 的数据绑定机制自动将请求参数转换为 Java 对象。

数据绑定流程

HTTP 请求参数 → 类型转换 → 数据格式化 → 数据校验 → 绑定到目标对象

类型转换

内置转换器

Spring MVC 内置了常用类型的转换器:
源类型目标类型
StringInteger, Long, Double, Boolean
StringDate, LocalDate, LocalDateTime
StringEnum
String数组, 集合

自定义转换器

// 实现 Converter 接口
public class StringToDateConverter implements Converter<String, Date> {
    
    private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    
    @Override
    public Date convert(String source) {
        try {
            return dateFormat.parse(source);
        } catch (ParseException e) {
            throw new IllegalArgumentException("日期格式错误");
        }
    }
}

// 注册转换器
@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new StringToDateConverter());
    }
}

数据格式化

@DateTimeFormat

public class User {
    private String name;
    
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday;
    
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
}

@NumberFormat

public class Product {
    private String name;
    
    @NumberFormat(pattern = "#,###.##")
    private Double price;
    
    @NumberFormat(style = NumberFormat.Style.PERCENT)
    private Double discount;
}

数据校验

添加依赖

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

常用校验注解

注解说明
@NotNull不能为 null
@NotEmpty不能为空
@NotBlank不能为空白
@Size长度范围
@Min / @Max数值范围
@Email邮箱格式
@Pattern正则匹配
@Past必须是过去的日期
@Future必须是将来的日期

使用示例

public class UserDTO {
    
    @NotBlank(message = "用户名不能为空")
    @Size(min = 2, max = 20, message = "用户名长度2-20")
    private String username;
    
    @NotBlank(message = "密码不能为空")
    @Size(min = 6, max = 20, message = "密码长度6-20")
    private String password;
    
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @Min(value = 0, message = "年龄不能小于0")
    @Max(value = 150, message = "年龄不能大于150")
    private Integer age;
}

@RestController
public class UserController {
    
    @PostMapping("/user")
    public Result createUser(@RequestBody @Valid UserDTO dto, BindingResult result) {
        if (result.hasErrors()) {
            String message = result.getFieldErrors().stream()
                    .map(FieldError::getDefaultMessage)
                    .collect(Collectors.joining(", "));
            return Result.error(400, message);
        }
        return Result.success();
    }
}

分组校验

// 定义分组
public interface Create {}
public interface Update {}

public class UserDTO {
    
    @Null(groups = Create.class)
    @NotNull(groups = Update.class)
    private Long id;
    
    @NotBlank(groups = {Create.class, Update.class})
    private String username;
}

@RestController
public class UserController {
    
    @PostMapping("/user")
    public Result create(@RequestBody @Validated(Create.class) UserDTO dto) {
        return Result.success();
    }
    
    @PutMapping("/user")
    public Result update(@RequestBody @Validated(Update.class) UserDTO dto) {
        return Result.success();
    }
}

自定义校验注解

// 定义注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {
    String message() default "手机号格式不正确";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

// 实现校验器
public class PhoneValidator implements ConstraintValidator<Phone, String> {
    
    private static final Pattern PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
    
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return true;
        }
        return PATTERN.matcher(value).matches();
    }
}

// 使用
public class UserDTO {
    @Phone
    private String phone;
}

@InitBinder

@InitBinder 用于初始化数据绑定器,可以进行类型转换、格式化等。
@Controller
public class UserController {
    
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        // 日期格式化
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
        
        // 禁止绑定某些字段
        binder.setDisallowedFields("id", "createTime");
    }
}

@ModelAttribute

@ModelAttribute 用于在请求处理前预处理数据。
@Controller
public class UserController {
    
    // 每次请求前执行
    @ModelAttribute
    public void addCommonAttributes(Model model) {
        model.addAttribute("appName", "MyApp");
    }
    
    // 从数据库获取对象
    @ModelAttribute("user")
    public User getUser(@RequestParam(required = false) Long id) {
        if (id != null) {
            return userService.findById(id);
        }
        return new User();
    }
    
    @PostMapping("/user")
    public String saveUser(@ModelAttribute("user") User user) {
        userService.save(user);
        return "success";
    }
}