SSM从入门到实战:3.3 SpringMVC数据绑定与验证

👋 大家好,我是 阿问学长!专注于分享优质开源项目解析、毕业设计项目指导支持、幼小初高教辅资料推荐等,欢迎关注交流!🚀

📖 本文概述

本文是SSM框架系列SpringMVC基础篇的第三篇,将深入探讨SpringMVC的数据绑定机制和数据验证功能。通过详细的代码示例和最佳实践,帮助读者掌握数据绑定的原理和验证框架的使用。

🎯 学习目标

  • 深入理解SpringMVC的数据绑定原理
  • 掌握各种数据类型的绑定方法
  • 学会使用Bean Validation进行数据验证
  • 了解自定义验证器的实现
  • 掌握数据转换和格式化技巧

1. 数据绑定原理

1.1 数据绑定概述

SpringMVC的数据绑定是将HTTP请求参数自动转换为Java对象的过程:

java 复制代码
/**
 * 数据绑定原理演示
 */
public class DataBindingPrinciple {
    
    /**
     * 传统Servlet方式处理表单数据
     */
    public void traditionalWay(HttpServletRequest request) {
        // 手动获取参数
        String username = request.getParameter("username");
        String email = request.getParameter("email");
        String ageStr = request.getParameter("age");
        
        // 手动类型转换
        Integer age = null;
        if (ageStr != null && !ageStr.isEmpty()) {
            try {
                age = Integer.parseInt(ageStr);
            } catch (NumberFormatException e) {
                // 处理转换异常
            }
        }
        
        // 手动创建对象
        User user = new User();
        user.setUsername(username);
        user.setEmail(email);
        user.setAge(age);
        
        // 手动验证
        if (username == null || username.trim().isEmpty()) {
            // 处理验证错误
        }
    }
    
    /**
     * SpringMVC数据绑定方式
     */
    @PostMapping("/user")
    public String springMvcWay(@Valid @ModelAttribute User user, 
                              BindingResult bindingResult) {
        // SpringMVC自动完成:
        // 1. 参数获取
        // 2. 类型转换
        // 3. 对象创建和属性设置
        // 4. 数据验证
        
        if (bindingResult.hasErrors()) {
            return "user/form";
        }
        
        // 直接使用绑定好的对象
        userService.save(user);
        return "redirect:/user/list";
    }
}

1.2 数据绑定流程

java 复制代码
/**
 * SpringMVC数据绑定流程详解
 */
public class DataBindingFlow {
    
    /**
     * 数据绑定的完整流程
     */
    public void bindingProcess() {
        /*
         * 1. 请求参数解析
         * HTTP请求: POST /user
         * Content-Type: application/x-www-form-urlencoded
         * Body: username=john&email=john@example.com&age=25&birthday=2023-01-01
         */
        
        /*
         * 2. 参数名称解析
         * SpringMVC解析出参数名称:
         * - username
         * - email  
         * - age
         * - birthday
         */
        
        /*
         * 3. 目标对象创建
         * 根据方法参数类型创建User对象实例
         */
        
        /*
         * 4. 属性路径解析
         * 将参数名称映射到对象属性路径:
         * - username -> user.username
         * - email -> user.email
         * - age -> user.age
         * - birthday -> user.birthday
         */
        
        /*
         * 5. 类型转换
         * 将字符串参数转换为目标类型:
         * - "john" -> String (无需转换)
         * - "25" -> Integer
         * - "2023-01-01" -> Date
         */
        
        /*
         * 6. 属性设置
         * 调用setter方法设置属性值:
         * - user.setUsername("john")
         * - user.setEmail("john@example.com")
         * - user.setAge(25)
         * - user.setBirthday(Date对象)
         */
        
        /*
         * 7. 数据验证
         * 如果有@Valid注解,执行Bean Validation
         */
        
        /*
         * 8. 绑定结果
         * 将绑定过程中的错误信息存储到BindingResult中
         */
    }
}

2. 基本数据类型绑定

2.1 简单类型绑定

java 复制代码
/**
 * 简单数据类型绑定演示
 */
@Controller
@RequestMapping("/binding")
public class SimpleTypeBindingController {
    
    /**
     * 基本数据类型绑定
     */
    @GetMapping("/simple")
    public String simpleTypes(
            String name,                    // 字符串类型
            int age,                       // 基本类型int
            Integer score,                 // 包装类型Integer
            boolean active,                // 布尔类型
            Boolean enabled,               // 包装类型Boolean
            double salary,                 // 双精度浮点
            BigDecimal amount,             // 大数值类型
            Model model) {
        
        model.addAttribute("name", name);
        model.addAttribute("age", age);
        model.addAttribute("score", score);
        model.addAttribute("active", active);
        model.addAttribute("enabled", enabled);
        model.addAttribute("salary", salary);
        model.addAttribute("amount", amount);
        
        return "binding/simple";
    }
    
    /**
     * 数组类型绑定
     */
    @GetMapping("/array")
    public String arrayTypes(
            String[] names,                // 字符串数组
            int[] scores,                  // 基本类型数组
            Integer[] ages,                // 包装类型数组
            Model model) {
        
        model.addAttribute("names", names);
        model.addAttribute("scores", scores);
        model.addAttribute("ages", ages);
        
        return "binding/array";
    }
    
    /**
     * 集合类型绑定
     */
    @GetMapping("/collection")
    public String collectionTypes(
            @RequestParam List<String> hobbies,        // List集合
            @RequestParam Set<String> skills,          // Set集合
            @RequestParam Map<String, String> params,  // Map集合
            Model model) {
        
        model.addAttribute("hobbies", hobbies);
        model.addAttribute("skills", skills);
        model.addAttribute("params", params);
        
        return "binding/collection";
    }
}

2.2 日期时间类型绑定

java 复制代码
/**
 * 日期时间类型绑定演示
 */
@Controller
@RequestMapping("/date")
public class DateTimeBindingController {
    
    /**
     * 全局日期格式配置
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
        
        // 时间戳格式
        SimpleDateFormat timestampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        timestampFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, "createTime", 
                                   new CustomDateEditor(timestampFormat, false));
    }
    
    /**
     * 日期类型绑定
     */
    @PostMapping("/submit")
    public String dateBinding(
            @DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday,           // 使用注解格式化
            @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date createTime, // 时间戳格式
            @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate localDate, // ISO日期格式
            @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime localDateTime, // ISO时间格式
            Model model) {
        
        model.addAttribute("birthday", birthday);
        model.addAttribute("createTime", createTime);
        model.addAttribute("localDate", localDate);
        model.addAttribute("localDateTime", localDateTime);
        
        return "date/result";
    }
    
    /**
     * 自定义日期转换器
     */
    @Component
    public static class StringToDateConverter implements Converter<String, Date> {
        
        private static final String[] DATE_PATTERNS = {
            "yyyy-MM-dd",
            "yyyy/MM/dd",
            "yyyy-MM-dd HH:mm:ss",
            "yyyy/MM/dd HH:mm:ss"
        };
        
        @Override
        public Date convert(String source) {
            if (source == null || source.trim().isEmpty()) {
                return null;
            }
            
            for (String pattern : DATE_PATTERNS) {
                try {
                    SimpleDateFormat format = new SimpleDateFormat(pattern);
                    format.setLenient(false);
                    return format.parse(source.trim());
                } catch (ParseException e) {
                    // 尝试下一个格式
                }
            }
            
            throw new IllegalArgumentException("无法解析日期: " + source);
        }
    }
}

3. 复杂对象绑定

3.1 嵌套对象绑定

java 复制代码
/**
 * 复杂对象绑定演示
 */
@Controller
@RequestMapping("/complex")
public class ComplexObjectBindingController {
    
    /**
     * 嵌套对象绑定
     */
    @PostMapping("/nested")
    public String nestedObjectBinding(@ModelAttribute UserForm userForm, 
                                    BindingResult bindingResult,
                                    Model model) {
        /*
         * 请求参数示例:
         * username=john
         * email=john@example.com
         * profile.realName=John Doe
         * profile.phone=1234567890
         * profile.address.province=Beijing
         * profile.address.city=Beijing
         * profile.address.street=Chaoyang Road
         */
        
        if (bindingResult.hasErrors()) {
            model.addAttribute("errors", bindingResult.getAllErrors());
            return "complex/form";
        }
        
        model.addAttribute("userForm", userForm);
        return "complex/result";
    }
    
    /**
     * 集合对象绑定
     */
    @PostMapping("/list")
    public String listObjectBinding(@ModelAttribute UserListForm form,
                                   BindingResult bindingResult,
                                   Model model) {
        /*
         * 请求参数示例:
         * users[0].username=user1
         * users[0].email=user1@example.com
         * users[0].age=25
         * users[1].username=user2
         * users[1].email=user2@example.com
         * users[1].age=30
         */
        
        if (bindingResult.hasErrors()) {
            return "complex/list-form";
        }
        
        model.addAttribute("form", form);
        return "complex/list-result";
    }
    
    /**
     * Map对象绑定
     */
    @PostMapping("/map")
    public String mapObjectBinding(@ModelAttribute UserMapForm form,
                                  Model model) {
        /*
         * 请求参数示例:
         * userMap['admin'].username=admin
         * userMap['admin'].email=admin@example.com
         * userMap['user'].username=user
         * userMap['user'].email=user@example.com
         */
        
        model.addAttribute("form", form);
        return "complex/map-result";
    }
}

/**
 * 表单对象定义
 */
public class UserForm {
    private String username;
    private String email;
    private UserProfile profile;
    
    // getter/setter...
}

public class UserProfile {
    private String realName;
    private String phone;
    private Address address;
    
    // getter/setter...
}

public class Address {
    private String province;
    private String city;
    private String street;
    
    // getter/setter...
}

public class UserListForm {
    private List<User> users = new ArrayList<>();
    
    // getter/setter...
}

public class UserMapForm {
    private Map<String, User> userMap = new HashMap<>();
    
    // getter/setter...
}

4. 数据验证

4.1 Bean Validation基础

java 复制代码
/**
 * Bean Validation基础验证
 */
public class User {
    
    @NotNull(message = "用户ID不能为空")
    private Long id;
    
    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度必须在3-20个字符之间")
    @Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只能包含字母、数字和下划线")
    private String username;
    
    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @NotBlank(message = "密码不能为空")
    @Size(min = 6, max = 20, message = "密码长度必须在6-20个字符之间")
    private String password;
    
    @Min(value = 18, message = "年龄不能小于18岁")
    @Max(value = 100, message = "年龄不能大于100岁")
    private Integer age;
    
    @DecimalMin(value = "0.0", message = "工资不能为负数")
    @DecimalMax(value = "999999.99", message = "工资不能超过999999.99")
    @Digits(integer = 6, fraction = 2, message = "工资格式不正确")
    private BigDecimal salary;
    
    @Past(message = "生日必须是过去的日期")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday;
    
    @Future(message = "过期时间必须是将来的日期")
    private Date expireTime;
    
    @AssertTrue(message = "必须同意用户协议")
    private Boolean agreeTerms;
    
    @Valid // 级联验证
    private UserProfile profile;
    
    @Valid
    @Size(min = 1, message = "至少需要一个角色")
    private List<Role> roles;
    
    // getter/setter...
}

/**
 * 用户资料验证
 */
public class UserProfile {
    
    @NotBlank(message = "真实姓名不能为空")
    @Size(max = 50, message = "真实姓名长度不能超过50个字符")
    private String realName;
    
    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
    private String phone;
    
    @URL(message = "头像URL格式不正确")
    private String avatar;
    
    @Valid
    private Address address;
    
    // getter/setter...
}

4.2 分组验证

java 复制代码
/**
 * 分组验证演示
 */
public class User {
    
    // 验证分组接口
    public interface Create {}
    public interface Update {}
    public interface Delete {}
    
    @NotNull(groups = {Update.class, Delete.class}, message = "更新和删除时ID不能为空")
    private Long id;
    
    @NotBlank(groups = {Create.class, Update.class}, message = "用户名不能为空")
    @Size(min = 3, max = 20, groups = {Create.class, Update.class}, 
          message = "用户名长度必须在3-20个字符之间")
    private String username;
    
    @NotBlank(groups = Create.class, message = "创建用户时密码不能为空")
    @Size(min = 6, max = 20, groups = Create.class, message = "密码长度必须在6-20个字符之间")
    private String password;
    
    @Email(groups = {Create.class, Update.class}, message = "邮箱格式不正确")
    private String email;
    
    // getter/setter...
}

/**
 * 控制器中使用分组验证
 */
@Controller
@RequestMapping("/user")
public class UserValidationController {
    
    /**
     * 创建用户 - 使用Create分组
     */
    @PostMapping("/create")
    public String createUser(@Validated(User.Create.class) @ModelAttribute User user,
                           BindingResult bindingResult,
                           Model model) {
        if (bindingResult.hasErrors()) {
            model.addAttribute("errors", bindingResult.getAllErrors());
            return "user/create";
        }
        
        userService.save(user);
        return "redirect:/user/list";
    }
    
    /**
     * 更新用户 - 使用Update分组
     */
    @PostMapping("/update")
    public String updateUser(@Validated(User.Update.class) @ModelAttribute User user,
                           BindingResult bindingResult,
                           Model model) {
        if (bindingResult.hasErrors()) {
            model.addAttribute("errors", bindingResult.getAllErrors());
            return "user/edit";
        }
        
        userService.update(user);
        return "redirect:/user/list";
    }
    
    /**
     * 删除用户 - 使用Delete分组
     */
    @PostMapping("/delete")
    public String deleteUser(@Validated(User.Delete.class) @ModelAttribute User user,
                           BindingResult bindingResult,
                           RedirectAttributes redirectAttributes) {
        if (bindingResult.hasErrors()) {
            redirectAttributes.addFlashAttribute("error", "删除失败:参数错误");
            return "redirect:/user/list";
        }
        
        userService.deleteById(user.getId());
        redirectAttributes.addFlashAttribute("message", "删除成功");
        return "redirect:/user/list";
    }
}

4.3 自定义验证器

java 复制代码
/**
 * 自定义验证注解
 */
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueUsernameValidator.class)
@Documented
public @interface UniqueUsername {
    String message() default "用户名已存在";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

/**
 * 自定义验证器实现
 */
@Component
public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {
    
    @Autowired
    private UserService userService;
    
    @Override
    public void initialize(UniqueUsername constraintAnnotation) {
        // 初始化方法,可以获取注解参数
    }
    
    @Override
    public boolean isValid(String username, ConstraintValidatorContext context) {
        if (username == null || username.trim().isEmpty()) {
            return true; // 空值由@NotBlank验证
        }
        
        // 检查用户名是否已存在
        return !userService.existsByUsername(username);
    }
}

/**
 * 复杂自定义验证注解
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PasswordMatchValidator.class)
@Documented
public @interface PasswordMatch {
    String message() default "密码和确认密码不匹配";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    
    String password();
    String confirmPassword();
}

/**
 * 密码匹配验证器
 */
public class PasswordMatchValidator implements ConstraintValidator<PasswordMatch, Object> {
    
    private String passwordField;
    private String confirmPasswordField;
    
    @Override
    public void initialize(PasswordMatch constraintAnnotation) {
        this.passwordField = constraintAnnotation.password();
        this.confirmPasswordField = constraintAnnotation.confirmPassword();
    }
    
    @Override
    public boolean isValid(Object obj, ConstraintValidatorContext context) {
        try {
            Object password = getFieldValue(obj, passwordField);
            Object confirmPassword = getFieldValue(obj, confirmPasswordField);
            
            if (password == null && confirmPassword == null) {
                return true;
            }
            
            if (password != null && password.equals(confirmPassword)) {
                return true;
            }
            
            // 自定义错误消息
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
                   .addPropertyNode(confirmPasswordField)
                   .addConstraintViolation();
            
            return false;
            
        } catch (Exception e) {
            return false;
        }
    }
    
    private Object getFieldValue(Object obj, String fieldName) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        return field.get(obj);
    }
}

/**
 * 使用自定义验证的表单对象
 */
@PasswordMatch(password = "password", confirmPassword = "confirmPassword")
public class UserRegistrationForm {
    
    @UniqueUsername
    @NotBlank(message = "用户名不能为空")
    private String username;
    
    @NotBlank(message = "密码不能为空")
    @Size(min = 6, max = 20, message = "密码长度必须在6-20个字符之间")
    private String password;
    
    @NotBlank(message = "确认密码不能为空")
    private String confirmPassword;
    
    @Email(message = "邮箱格式不正确")
    private String email;
    
    // getter/setter...
}

5. 数据转换和格式化

5.1 类型转换器

java 复制代码
/**
 * 自定义类型转换器
 */
@Component
public class StringToUserConverter implements Converter<String, User> {
    
    @Autowired
    private UserService userService;
    
    @Override
    public User convert(String source) {
        if (source == null || source.trim().isEmpty()) {
            return null;
        }
        
        try {
            Long userId = Long.parseLong(source);
            return userService.findById(userId);
        } catch (NumberFormatException e) {
            // 尝试按用户名查找
            return userService.findByUsername(source);
        }
    }
}

/**
 * 枚举转换器
 */
@Component
public class StringToUserStatusConverter implements Converter<String, UserStatus> {
    
    @Override
    public UserStatus convert(String source) {
        if (source == null || source.trim().isEmpty()) {
            return null;
        }
        
        try {
            // 尝试按序号转换
            int ordinal = Integer.parseInt(source);
            UserStatus[] values = UserStatus.values();
            if (ordinal >= 0 && ordinal < values.length) {
                return values[ordinal];
            }
        } catch (NumberFormatException e) {
            // 尝试按名称转换
            try {
                return UserStatus.valueOf(source.toUpperCase());
            } catch (IllegalArgumentException ex) {
                // 忽略异常,返回null
            }
        }
        
        return null;
    }
}

/**
 * 转换器配置
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new StringToUserConverter());
        registry.addConverter(new StringToUserStatusConverter());
        registry.addFormatter(new DateFormatter("yyyy-MM-dd"));
        registry.addFormatter(new NumberStyleFormatter("#,##0.00"));
    }
}

5.2 格式化器

java 复制代码
/**
 * 自定义格式化器
 */
@Component
public class MoneyFormatter implements Formatter<BigDecimal> {
    
    @Override
    public BigDecimal parse(String text, Locale locale) throws ParseException {
        if (text == null || text.trim().isEmpty()) {
            return null;
        }
        
        // 移除货币符号和千分位分隔符
        String cleanText = text.replaceAll("[¥$,]", "");
        
        try {
            return new BigDecimal(cleanText);
        } catch (NumberFormatException e) {
            throw new ParseException("无法解析金额: " + text, 0);
        }
    }
    
    @Override
    public String print(BigDecimal money, Locale locale) {
        if (money == null) {
            return "";
        }
        
        NumberFormat formatter = NumberFormat.getCurrencyInstance(locale);
        return formatter.format(money);
    }
}

/**
 * 使用格式化注解
 */
public class Product {
    
    @NumberFormat(style = NumberFormat.Style.CURRENCY)
    private BigDecimal price;
    
    @NumberFormat(pattern = "#,##0.00")
    private BigDecimal weight;
    
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date createDate;
    
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
    private LocalDateTime updateTime;
    
    // getter/setter...
}

6. 错误处理和国际化

6.1 验证错误处理

java 复制代码
/**
 * 验证错误处理
 */
@Controller
@RequestMapping("/validation")
public class ValidationErrorController {
    
    /**
     * 处理验证错误
     */
    @PostMapping("/submit")
    public String handleValidation(@Valid @ModelAttribute UserForm userForm,
                                 BindingResult bindingResult,
                                 Model model) {
        
        if (bindingResult.hasErrors()) {
            // 获取所有错误
            List<ObjectError> allErrors = bindingResult.getAllErrors();
            
            // 获取字段错误
            List<FieldError> fieldErrors = bindingResult.getFieldErrors();
            
            // 获取全局错误
            List<ObjectError> globalErrors = bindingResult.getGlobalErrors();
            
            // 构建错误信息
            Map<String, String> errorMap = new HashMap<>();
            for (FieldError error : fieldErrors) {
                errorMap.put(error.getField(), error.getDefaultMessage());
            }
            
            model.addAttribute("errors", errorMap);
            model.addAttribute("globalErrors", globalErrors);
            
            return "validation/form";
        }
        
        return "validation/success";
    }
    
    /**
     * AJAX验证错误处理
     */
    @PostMapping("/ajax-submit")
    @ResponseBody
    public ResponseEntity<?> handleAjaxValidation(@Valid @RequestBody UserForm userForm,
                                                 BindingResult bindingResult) {
        
        if (bindingResult.hasErrors()) {
            Map<String, Object> response = new HashMap<>();
            Map<String, String> errors = new HashMap<>();
            
            for (FieldError error : bindingResult.getFieldErrors()) {
                errors.put(error.getField(), error.getDefaultMessage());
            }
            
            response.put("success", false);
            response.put("errors", errors);
            
            return ResponseEntity.badRequest().body(response);
        }
        
        // 处理成功
        Map<String, Object> response = new HashMap<>();
        response.put("success", true);
        response.put("message", "提交成功");
        
        return ResponseEntity.ok(response);
    }
}

6.2 国际化支持

properties 复制代码
# messages.properties (默认)
user.username.notblank=用户名不能为空
user.username.size=用户名长度必须在{min}-{max}个字符之间
user.email.email=邮箱格式不正确
user.age.min=年龄不能小于{value}岁

# messages_en.properties (英文)
user.username.notblank=Username cannot be blank
user.username.size=Username length must be between {min}-{max} characters
user.email.email=Email format is incorrect
user.age.min=Age cannot be less than {value} years old

# messages_zh_CN.properties (中文)
user.username.notblank=用户名不能为空
user.username.size=用户名长度必须在{min}-{max}个字符之间
user.email.email=邮箱格式不正确
user.age.min=年龄不能小于{value}岁
java 复制代码
/**
 * 国际化配置
 */
@Configuration
public class InternationalizationConfig {
    
    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("messages");
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }
    
    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver resolver = new SessionLocaleResolver();
        resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
        return resolver;
    }
    
    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
        interceptor.setParamName("lang");
        return interceptor;
    }
}

/**
 * 使用国际化的验证
 */
public class User {
    
    @NotBlank(message = "{user.username.notblank}")
    @Size(min = 3, max = 20, message = "{user.username.size}")
    private String username;
    
    @Email(message = "{user.email.email}")
    private String email;
    
    @Min(value = 18, message = "{user.age.min}")
    private Integer age;
    
    // getter/setter...
}

7. 小结

本文深入介绍了SpringMVC的数据绑定和验证机制:

  1. 数据绑定原理:从HTTP参数到Java对象的自动转换过程
  2. 基本类型绑定:简单类型、数组、集合、日期时间的绑定
  3. 复杂对象绑定:嵌套对象、集合对象、Map对象的绑定
  4. 数据验证:Bean Validation、分组验证、自定义验证器
  5. 类型转换:自定义转换器和格式化器
  6. 错误处理:验证错误处理和国际化支持

掌握数据绑定和验证的关键点:

  • 理解数据绑定的完整流程
  • 正确使用各种验证注解
  • 合理设计验证分组
  • 实现自定义验证逻辑
  • 处理验证错误和用户体验

🔗 下一篇预告

下一篇文章将介绍SpringMVC视图解析与模板引擎,学习如何处理视图渲染和模板技术的集成。


相关文章: