SpringMVC学习笔记 | 数据转换、数据格式化、数据校验

数据绑定

数据绑定流程

  • SpringMVC主框架将ServletRequest对象及目标方法的入参实例传递给WebDataBinderFactory实例,以创建DataBinder实例对象。
  • DataBinder调用装配在SpringMVC上下文中的ConversionService组件进行数据类型转换、数据格式化工作,将Servlet中的请求信息填充到入参对象中。
  • 调用Validator组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果BindingData对象。
  • SpringMVC抽取BindingResult中的入参对象和校验错误对象,将他们赋给处理方法的响应入参。

类型转换

自定义类型转换器

ConversionService是Spring类型转换体系的核心接口,可以利用ConversionServiceFactoryBean在Spring的IOC容器中定义一个ConversionService。Spring将自动识别出IOC容器中的ConversionService,并在bean属性配置及SpringMVC处理方法入参绑定等场合使用它进行数据的转换
可以通过ConversionServiceFactoryBeanconverters属性注册自定义的类型转换器

<bean id="conversionService" 
        class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <ref bean="employeeConverter"/>
        </set>
    </property>
</bean>

Spring支持的转换器

Spring定义了3种类型的转换器接口,实现任意一个转换器接口都可以作为自定义转换器注册到ConversionServiceFactroyBean

  • Converter<S,T>:将S类型对象转为T类型对象
  • ConverterFactory:将相同系列多个“同质”Converter封装在一起,如果希望将一种类型的对象转换为另一种类型及其子类的对象(例如将String转换为Number及Number子类(Integer、Long、Double等)对象)可使用该转换器工厂类。
  • GenericConverter:会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换。

在配置文件中使用<mvc:annotation-driven conversion-service="xxxxx"/>会将自定义的ConversionService注册到SpringMVC的上下文中。,其中xxxxx是自定义的类型转换器名,例如:

<mvc:annotation-driven conversion-service="conversionService"/>
<!-- 配置ConversionService-->
<bean id="conversionService" 
          class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
         <set>
             <ref bean="employeeConverter"/>
         </set>
    </property>
</bean>

我们来自定义一个类型转换器,我们希望输入一个字符串(lastname-email-gender-department.id),然后提交后能转换为一个Employee对象。

  • 首先建立我们的两个实体类及对应的dao类
package com.cerr.springmvc.crud.entities;

public class Employee {
    private Integer id;
    private String lastname;
    private String email;
    private Integer gender;
    private Department department;

    public Employee(){}

    public Employee(Integer id, String lastname, String email, Integer gender, Department department) {
        this.id = id;
        this.lastname = lastname;
        this.email = email;
        this.gender = gender;
        this.department = department;
    }

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getLastname() {
        return lastname;
    }
    public void setLastname(String lastname) {
        this.lastname = lastname;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public Integer getGender() {
        return gender;
    }
    public void setGender(Integer gender) {
        this.gender = gender;
    }
    public Department getDepartment() {
        return department;
    }
    public void setDepartment(Department department) {
        this.department = department;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", lastname='" + lastname + '\'' +
                ", email='" + email + '\'' +
                ", gender=" + gender +
                ", department=" + department +
                '}';
    }
}
package com.cerr.springmvc.crud.entities;

public class Department {
    private Integer id;
    private String departmentName;

    public Department() {}

    public Department(Integer id, String departmentName) {
        super();
        this.id = id;
        this.departmentName = departmentName;
    }

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getDepartmentName() {
        return departmentName;
    }
    public void setDepartmentName(String departmentName) {
        this.departmentName = departmentName;
    }
}
package com.cerr.springmvc.crud.dao;

import com.cerr.springmvc.crud.entities.Department;
import com.cerr.springmvc.crud.entities.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Repository
public class EmployeeDao {

    private static Map <Integer, Employee > employees = null;

    @Autowired
    private DepartmentDao departmentDao;

    static{
        employees = new HashMap <Integer, Employee>();

        employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1, new Department(101, "D-AA")));
        employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1, new Department(102, "D-BB")));
        employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0, new Department(103, "D-CC")));
        employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0, new Department(104, "D-DD")));
        employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1, new Department(105, "D-EE")));
    }

    private static Integer initId = 1006;

    public void save(Employee employee){
        if(employee.getId() == null){
            employee.setId(initId++);
        }

        employee.setDepartment(departmentDao.getDepartment(employee.getDepartment().getId()));
        employees.put(employee.getId(), employee);
    }

    public Collection <Employee> getAll(){
        return employees.values();
    }

    public Employee get(Integer id){
        return employees.get(id);
    }

    public void delete(Integer id){
        employees.remove(id);
    }
}
  • 建立我们自定义的转换器类,需要实现Converter<S,T>接口
package com.cerr.springmvc.converters;

import com.cerr.springmvc.crud.entities.Department;
import com.cerr.springmvc.crud.entities.Employee;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

//该注释让该类能加载到IOC容器中
@Component
public class EmployeeConverter implements Converter<String, Employee > {

    /**
     * 这是转换的方法,对传入的字符串进行分割。
     * 然后填充Employee的参数来初始化一个Employee实例,然后返回
     * @param s
     * @return
     */
    @Override
    public Employee convert(String s) {
        if (s != null){
            String [] vals = s.split("-");
            if (vals != null && vals.length == 4){
                String lastName = vals[0];
                String email = vals[1];
                Integer gender = Integer.parseInt(vals[2]);
                Department department = new Department();
                department.setId(Integer.parseInt(vals[3]));
                Employee employee = new Employee(null,lastName,email,gender,department);
                System.out.println(employee);
                return employee;
            }
        }
        return null;
    }
}
  • 在配置文件中配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 配置自动扫描的包-->
    <context:component-scan base-package="com.cerr.springmvc"/>

    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/webViews/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <mvc:default-servlet-handler/>
    <mvc:annotation-driven conversion-service="conversionService"/>

    <!-- 配置ConversionService-->
    <bean id="conversionService"
          class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <!-- 
                因为我们自己写的自定义转换器类使用了Component进行标识,
                所以此处的bean名直接写employeeConverter(类名第一个字母小写)即可
                -->
                <ref bean="employeeConverter"/>
            </set>
        </property>
    </bean>
</beans>

测试:

package com.cerr.springmvc.test;

import com.cerr.springmvc.crud.dao.EmployeeDao;
import com.cerr.springmvc.crud.entities.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class SpringMVCTest1 {
    @Autowired
    private EmployeeDao employeeDao;
    @RequestMapping(value = "/testConversionServiceConverer",method = RequestMethod.POST)
    public String testConverter(@RequestParam("employee") Employee employee){
        employeeDao.save(employee);
        System.out.println(employee);
        return "redirect:/emps";
    }
}

表单:

<%@ page import="java.util.Map" %>
<%@ page import="java.util.HashMap" %>
<%--
  Created by IntelliJ IDEA.
  User: 白菜
  Date: 2019/11/13
  Time: 21:10
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="${pageContext.request.contextPath}/testConversionServiceConverer" method="POST">
        <!-- 格式:lastname-email-gender-department.id 例如:GG-gg@cerr.com-0-105 -->
        Employee : <input type="text" name="employee" />
        <input type="submit" value="submit" />
    </form>
</body>
</html>

输入:GG-gg@cerr.com-0-105
结果:在控制台成功打印出Employee(转换成功)

关于mvc:annotation-driven

<mvc:annotation-driven/>会自动注册RequestMappingHandlerMappingRequestMappingHandlerAdapterExceptionHandlerExceptionResolver三个bean。
还将提供以下支持:

  • 支持使用ConversionService实例对表单参数进行类型转换
  • 支持使用@NumberFormatannotation@DateTimeFormat注解完成数据类型的格式化
  • 支持使用@Valid注解对JavaBean实例进行JSR 303验证
  • 支持使用@RequestBody@ResponseBody注解

@InitBinder

@InitBinder标识的方法,可以对WebDataBinder对象进行初始化。WebDataBinder是DataBinder的子类,用于完成由表单字段到JavaBean属性的绑定。
@InitBinder方法不能有返回值,必须声明为void;@InitBinder方法的参数通常是WebDataBinder

例如我们这里定义一个initBinder方法,然后方法体里面的设置对于表单的lastname属性不允许绑定到JavaBean顺序。

@InitBinder
    public void initBinder(WebDataBinder binder){
        binder.setDisallowedFields("lastname");
    }

数据格式化

对属性对象的输入/输出进行格式化,从其本质上讲依然属于“类型转换”的范畴。Spring在格式化模块中定义了一个实现ConversionService接口的FarmattingConversionService实现类,该实现类扩展了GenericConversionService,因此它既具有类型转换的功能,又具有格式化的功能FormattingConversionService拥有一个FormattingConversionServiceFactoryBean工厂类,后者用于在Spring上下文中构造前者。

对于FormattingConversionServiceFactoryBean,内部已经注册了:

  • NumberFormatAnnotationFormatterFactory:支持对数字类型属性使用@NumberFormat注解
  • JodaDateTimeFormatAnnotationFormatterFactory:支持对日期类型的属性使用@DataTimeFormat注解

如果装配了FormattingConversionServiceFactoryBean,就可以在SpringMVC入参绑定及模型数据输出时使用注解驱动了,<mvc:annotation-driven />默认创建的ConversionService实例即为FormattingConversionServiceFactoryBean

日期格式化

@DateTimeFOrmat注解可对java.util.Datejava.util.Calendarjava.long.Long时间类型进行标注,比较重要的属性:

  • pattern属性:类型为字符串,指定解析/格式化字段数据的模式,如:"yyyy-MM-dd hh:mm:ss"
package com.cerr.springmvc.crud.entities;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;
import java.util.Date;

public class Employee {
    private Integer id;
    private String lastname;
    private String email;
    private Integer gender;
    private Department department;

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birth;
    private Float sarly;
    public Employee(){}

    public Employee(Integer id, String lastname, String email, Integer gender, Department department) {
        this.id = id;
        this.lastname = lastname;
        this.email = email;
        this.gender = gender;
        this.department = department;
    }

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    public Float getSarly() {
        return sarly;
    }

    public void setSarly(Float sarly) {
        this.sarly = sarly;
    }

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getLastname() {
        return lastname;
    }
    public void setLastname(String lastname) {
        this.lastname = lastname;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public Integer getGender() {
        return gender;
    }
    public void setGender(Integer gender) {
        this.gender = gender;
    }
    public Department getDepartment() {
        return department;
    }
    public void setDepartment(Department department) {
        this.department = department;
    }
    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", lastname='" + lastname + '\'' +
                ", email='" + email + '\'' +
                ", gender=" + gender +
                ", department=" + department +
                ", birth=" + birth +
                ", sarly=" + sarly +
                '}';
    }
}

在上面的例子中,我们注解的pattern为"yyyy-MM-dd",因此当我们输入的日期为"yyyy-MM-dd"时都能给解析并转换成Date

数值格式化

@NumberFormat可对类似数字类型的属性进行标注,它拥有两个互斥的属性:

  • style
    类型为Numberformat.Style。用于指定样式类型,包括三种:Style.NUMBER(正常数字类型)、Style.CURRENCY(货币类型)、Style.PERCENT(百分数类型)。
  • pattern
    类型为String,自定义样式,如pattern="#,###"
package com.cerr.springmvc.crud.entities;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;
import java.util.Date;

public class Employee {
    private Integer id;
    private String lastname;
    private String email;
    private Integer gender;
    private Department department;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birth;
    @NumberFormat(pattern = "#,###,###.#")
    private Float sarly;

    public Employee(){}

    public Employee(Integer id, String lastname, String email, Integer gender, Department department) {
        this.id = id;
        this.lastname = lastname;
        this.email = email;
        this.gender = gender;
        this.department = department;
    }

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    public Float getSarly() {
        return sarly;
    }

    public void setSarly(Float sarly) {
        this.sarly = sarly;
    }

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getLastname() {
        return lastname;
    }
    public void setLastname(String lastname) {
        this.lastname = lastname;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public Integer getGender() {
        return gender;
    }
    public void setGender(Integer gender) {
        this.gender = gender;
    }
    public Department getDepartment() {
        return department;
    }
    public void setDepartment(Department department) {
        this.department = department;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", lastname='" + lastname + '\'' +
                ", email='" + email + '\'' +
                ", gender=" + gender +
                ", department=" + department +
                ", birth=" + birth +
                ", sarly=" + sarly +
                '}';
    }
}

上面的代码标识了@NumberFormat(pattern = "#,###,###.#")后,例如输入1,234,567.8,就能自动转成数字。


数据校验

JSR303

JSR 303是Java为Bean数据合法性校验提供的标准框架,它已经包含在JavaEE6.0中,JSR 303通过在Bean属性上标注类似于@NotNull@Max等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证。

注解 功能说明
@Null 被注释的元素必须为null
@NotNull 被注释的元素必须不为null
@AssertTure 被注释的元素必须为true
@AssertFalse 被注释的元素必须为false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max,min) 被注释的元素必须在指定的范围内
@Digits(integer,fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(value) 被注释的元素必须符合指定的正则表达式

Hibernate Validator扩展注解

Hibernate Validator是JSR 303的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解

注解 功能说明
@Email 被注释的元素必须是电子邮箱地址
@Length 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串必须非空
@Range 被注释的元素必须在合适的范围内

SpringMVC数据校验

Spring4.0拥有自己独立的数据校验框架,同时支持JSR 303标准的校验框架。Spring在进行数据绑定时,可同时调用校验框架完成数据校验工作。在SpringMVC中,可直接通过注解驱动的方式进行数据校验

Spring的LocalValidatorFactoryBean既实现了Spring的Validator接口,也实现了JSR 303的Validator接口,只要在Spring容器中定义一个LocalValidatorFactoryBean,即可将其注入到需要数据校验的Bean中。

Spring本身并没有提供JSR 303的实现,所以必须将JSR 303的jar包放到类路径下

<mvc:annotation-driven />会默认装配好一个LocalValidatorFactroyBean,通过在处理方法的入参上标注@valid注解即可让SpringMVC在完成数据绑定后执行数据校验的工作。

在已经标注了JSR 303注解的表单/命令对象前标注一个@Valid,SpringMVC框架在将请求参数绑定到该入参对象后,就会调用校验框架根据注解声明的校验规则实施校验。

SpringMVC是通过对处理方法签约的规约来保存校验结果的:前一个表单/命令对象的校验结果保存到随后的入参中,这个保存校验结果的入参必须是BindingResultErrors类型,这两个类都位于org.springframework.validation包中。要注意的一点是:需要校验的Bean对象和其绑定结果对象或错误对象是成对出现的,它们之间不允许声明其他的入参
例如下面的代码是错误的:

@RequestMapping(value = "/emp",method = RequestMethod.POST)
    public String save(@Valid Employee employee, Map<String,Object> map,BindingResult result){
       
        return "redirect:/emps";
    }

Employee和BindingResult两个参数应该是在一起的,例如下面的才是正确的:

@RequestMapping(value = "/emp",method = RequestMethod.POST)
    public String save(@Valid Employee employee, BindingResult result,Map<String,Object> map){
        
        return "redirect:/emps";
    }

Errors接口提供了获取错误信息的方法,如getErrorCount()getFieldErrors(String field)BindingResult扩展了Errors接口。

@RequestMapping(value = "/emp",method = RequestMethod.POST)
    public String save(@Valid Employee employee, BindingResult result,Map<String,Object> map){
        System.out.println("save:"+employee);
        /**
         * 如果转换出错
         */
        if (result.getErrorCount() > 0){
            System.out.println("出错了:");
            for (FieldError error : result.getFieldErrors()){
                System.out.println(error.getField() + ":" + error.getDefaultMessage());
            }
            map.put("departments",departmentDao.getDepartments());
            
            //若验证出错,则转向定制的页面
            return "input";
        }
        employeeDao.save(employee);
        return "redirect:/emps";
    }

SpringMVC整合扩展的Hibernate Validator验证框架数据校验的步骤

  • 使用JSR 303验证标准完成数据校验。
  • 加入hibernate validator验证框架的jar包。
  • 在SpringMVC配置文件中添加<mvc:annotation-dirven />
  • 需要在bean的属性上添加对应的注解
  • 在目标方法bean类型的前面添加@Valid注解

首先在idea中加入jar包,并添加在Artifacts中


然后在配置文件中添加<mvc:annotation-dirven />,因为该配置在上面类型转换时定义了一个自定义的类型转换器,因此配置了FormattingConversionServiceFactoryBean,如果不需要使用自定义类型转换器的话,这部分代码可以去掉:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 配置自动扫描的包-->
    <context:component-scan base-package="com.cerr.springmvc"/>
    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/webViews/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <mvc:default-servlet-handler/>
    <mvc:annotation-driven conversion-service="conversionService"/>

    <!-- 配置ConversionService   org.springframework.context.support.ConversionServiceFactoryBean也可以-->
    <bean id="conversionService"
          class="org.springframework.format.support.FormattingConversionServiceFactoryBean">

        <property name="converters">
            <set>
                <ref bean="employeeConverter"/>
            </set>
        </property>
    </bean>
</beans>

对上面的Employee类,我们可以做一些验证如下,添加@NotNull@Email@Past注解:

package com.cerr.springmvc.crud.entities;

import org.hibernate.validator.constraints.Email;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import java.util.Date;

public class Employee {
    private Integer id;

    @NotNull
    private String lastname;
    @Email
    private String email;

    private Integer gender;
    private Department department;

    @Past
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birth;
    @NumberFormat(pattern = "#,###,###.#")
    private Float sarly;

    public Employee(){}

    public Employee(Integer id, String lastname, String email, Integer gender, Department department) {
        this.id = id;
        this.lastname = lastname;
        this.email = email;
        this.gender = gender;
        this.department = department;
    }

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    public Float getSarly() {
        return sarly;
    }

    public void setSarly(Float sarly) {
        this.sarly = sarly;
    }

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getLastname() {
        return lastname;
    }
    public void setLastname(String lastname) {
        this.lastname = lastname;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public Integer getGender() {
        return gender;
    }
    public void setGender(Integer gender) {
        this.gender = gender;
    }
    public Department getDepartment() {
        return department;
    }
    public void setDepartment(Department department) {
        this.department = department;
    }


    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", lastname='" + lastname + '\'' +
                ", email='" + email + '\'' +
                ", gender=" + gender +
                ", department=" + department +
                ", birth=" + birth +
                ", sarly=" + sarly +
                '}';
    }
}

并在处理的方法中的入参中对要校验的实体类标注@Valid,如果需要使用到BindingResult等类的话要注意要和@Valid标注的实体类成对存在:

@RequestMapping(value = "/emp",method = RequestMethod.POST)
    public String save(@Valid Employee employee, BindingResult result,Map<String,Object> map){
        System.out.println("save:"+employee);
        /**
         * 如果转换出错
         */
        if (result.getErrorCount() > 0){
            System.out.println("出错了:");
            for (FieldError error : result.getFieldErrors()){
                System.out.println(error.getField() + ":" + error.getDefaultMessage());
            }
            map.put("departments",departmentDao.getDepartments());
            
            //若验证出错,则转向定制的页面
            return "input";
        }
        employeeDao.save(employee);
        return "redirect:/emps";
    }

如果输入错误,则输出结果如下:


在页面上显示错误

在JSP页面上通过<form:errors path="属性名" />显示错误消息

<form:errors path="lastname"/><br>

如果要一次性显示所有错误消息,可以将path写为*,即:

<form:errors path="*"/><br>

提示消息的国际化

每个属性在数据绑定和数据校验发生错误时,都会生成一个对应的FieldError对象。当一个属性校验失败后,校验框架会为该属性生成4个消息代码,这些代码以校验注解类名为前缀,结合modleAttribute、属性名及属性类型名生成多个对应的消息代码。
例如User类中的password属性标注了一个@Pattern注解,当该属性值不满足@Pattern所定义的规则时,就会产生以下4个错误代码:

  • Pattern.user.password
  • Pattern.password
  • Pattern.java.lang.String
  • Pattern

当使用SpringMVC标签显示错误消息时,SpringMVC会查看WEB上下文是否装配了对应的国际化消息,如果没有,则显示默认的错误消息,否则使用国际化消息。

定制提示消息的话,我们要先定义一个国际化资源文件,例如i18n.properties文件,并且写入需要定制化的消息:

NotNull.employee.lastname=lastname\u4e0d\u80fd\u4e3a\u7a7a
Email.employee.email=Email\u4e0d\u5408\u6cd5
Past.employee.birth=Birth\u4e0d\u80fd\u662f\u4e00\u4e2a\u5c06\u6765\u7684\u65f6\u95f4
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容