SpringMVC學(xué)習(xí)筆記 | 數(shù)據(jù)轉(zhuǎn)換饵筑、數(shù)據(jù)格式化埃篓、數(shù)據(jù)校驗(yàn)

數(shù)據(jù)綁定

數(shù)據(jù)綁定流程

  • SpringMVC主框架將ServletRequest對(duì)象及目標(biāo)方法的入?yún)?shí)例傳遞給WebDataBinderFactory實(shí)例,以創(chuàng)建DataBinder實(shí)例對(duì)象根资。
  • DataBinder調(diào)用裝配在SpringMVC上下文中的ConversionService組件進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換架专、數(shù)據(jù)格式化工作同窘,將Servlet中的請(qǐng)求信息填充到入?yún)?duì)象中。
  • 調(diào)用Validator組件對(duì)已經(jīng)綁定了請(qǐng)求消息的入?yún)?duì)象進(jìn)行數(shù)據(jù)合法性校驗(yàn)部脚,并最終生成數(shù)據(jù)綁定結(jié)果BindingData對(duì)象想邦。
  • SpringMVC抽取BindingResult中的入?yún)?duì)象和校驗(yàn)錯(cuò)誤對(duì)象,將他們賦給處理方法的響應(yīng)入?yún)ⅰ?/li>

類型轉(zhuǎn)換

自定義類型轉(zhuǎn)換器

ConversionService是Spring類型轉(zhuǎn)換體系的核心接口委刘,可以利用ConversionServiceFactoryBean在Spring的IOC容器中定義一個(gè)ConversionService丧没。Spring將自動(dòng)識(shí)別出IOC容器中的ConversionService,并在bean屬性配置及SpringMVC處理方法入?yún)⒔壎ǖ葓?chǎng)合使用它進(jìn)行數(shù)據(jù)的轉(zhuǎn)換锡移。
可以通過(guò)ConversionServiceFactoryBeanconverters屬性注冊(cè)自定義的類型轉(zhuǎn)換器呕童。

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

Spring支持的轉(zhuǎn)換器

Spring定義了3種類型的轉(zhuǎn)換器接口,實(shí)現(xiàn)任意一個(gè)轉(zhuǎn)換器接口都可以作為自定義轉(zhuǎn)換器注冊(cè)到ConversionServiceFactroyBean

  • Converter<S,T>:將S類型對(duì)象轉(zhuǎn)為T(mén)類型對(duì)象淆珊。
  • ConverterFactory:將相同系列多個(gè)“同質(zhì)”Converter封裝在一起夺饲,如果希望將一種類型的對(duì)象轉(zhuǎn)換為另一種類型及其子類的對(duì)象(例如將String轉(zhuǎn)換為Number及Number子類(Integer、Long施符、Double等)對(duì)象)可使用該轉(zhuǎn)換器工廠類往声。
  • GenericConverter:會(huì)根據(jù)源類對(duì)象及目標(biāo)類對(duì)象所在的宿主類中的上下文信息進(jìn)行類型轉(zhuǎn)換。

在配置文件中使用<mvc:annotation-driven conversion-service="xxxxx"/>會(huì)將自定義的ConversionService注冊(cè)到SpringMVC的上下文中操刀。烁挟,其中xxxxx是自定義的類型轉(zhuǎn)換器名,例如:

<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>

我們來(lái)自定義一個(gè)類型轉(zhuǎn)換器骨坑,我們希望輸入一個(gè)字符串(lastname-email-gender-department.id)撼嗓,然后提交后能轉(zhuǎn)換為一個(gè)Employee對(duì)象。

  • 首先建立我們的兩個(gè)實(shí)體類及對(duì)應(yīng)的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);
    }
}
  • 建立我們自定義的轉(zhuǎn)換器類欢唾,需要實(shí)現(xiàn)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 > {

    /**
     * 這是轉(zhuǎn)換的方法且警,對(duì)傳入的字符串進(jìn)行分割。
     * 然后填充Employee的參數(shù)來(lái)初始化一個(gè)Employee實(shí)例礁遣,然后返回
     * @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">

    <!-- 配置自動(dòng)掃描的包-->
    <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>
                <!-- 
                因?yàn)槲覀冏约簩?xiě)的自定義轉(zhuǎn)換器類使用了Component進(jìn)行標(biāo)識(shí)斑芜,
                所以此處的bean名直接寫(xiě)employeeConverter(類名第一個(gè)字母小寫(xiě))即可
                -->
                <ref bean="employeeConverter"/>
            </set>
        </property>
    </bean>
</beans>

測(cè)試:

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
結(jié)果:在控制臺(tái)成功打印出Employee(轉(zhuǎn)換成功)

關(guān)于mvc:annotation-driven

<mvc:annotation-driven/>會(huì)自動(dòng)注冊(cè)RequestMappingHandlerMappingRequestMappingHandlerAdapterExceptionHandlerExceptionResolver三個(gè)bean祟霍。
還將提供以下支持:

  • 支持使用ConversionService實(shí)例對(duì)表單參數(shù)進(jìn)行類型轉(zhuǎn)換
  • 支持使用@NumberFormatannotation杏头、@DateTimeFormat注解完成數(shù)據(jù)類型的格式化
  • 支持使用@Valid注解對(duì)JavaBean實(shí)例進(jìn)行JSR 303驗(yàn)證
  • 支持使用@RequestBody@ResponseBody注解

@InitBinder

@InitBinder標(biāo)識(shí)的方法,可以對(duì)WebDataBinder對(duì)象進(jìn)行初始化沸呐。WebDataBinder是DataBinder的子類醇王,用于完成由表單字段到JavaBean屬性的綁定。
@InitBinder方法不能有返回值崭添,必須聲明為void寓娩;@InitBinder方法的參數(shù)通常是WebDataBinder

例如我們這里定義一個(gè)initBinder方法,然后方法體里面的設(shè)置對(duì)于表單的lastname屬性不允許綁定到JavaBean順序棘伴。

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

數(shù)據(jù)格式化

對(duì)屬性對(duì)象的輸入/輸出進(jìn)行格式化寞埠,從其本質(zhì)上講依然屬于“類型轉(zhuǎn)換”的范疇。Spring在格式化模塊中定義了一個(gè)實(shí)現(xiàn)ConversionService接口的FarmattingConversionService實(shí)現(xiàn)類焊夸,該實(shí)現(xiàn)類擴(kuò)展了GenericConversionService仁连,因此它既具有類型轉(zhuǎn)換的功能,又具有格式化的功能阱穗。FormattingConversionService擁有一個(gè)FormattingConversionServiceFactoryBean工廠類怖糊,后者用于在Spring上下文中構(gòu)造前者。

對(duì)于FormattingConversionServiceFactoryBean颇象,內(nèi)部已經(jīng)注冊(cè)了:

  • NumberFormatAnnotationFormatterFactory:支持對(duì)數(shù)字類型屬性使用@NumberFormat注解
  • JodaDateTimeFormatAnnotationFormatterFactory:支持對(duì)日期類型的屬性使用@DataTimeFormat注解

如果裝配了FormattingConversionServiceFactoryBean,就可以在SpringMVC入?yún)⒔壎澳P蛿?shù)據(jù)輸出時(shí)使用注解驅(qū)動(dòng)了并徘,<mvc:annotation-driven />默認(rèn)創(chuàng)建的ConversionService實(shí)例即為FormattingConversionServiceFactoryBean遣钳。

日期格式化

@DateTimeFOrmat注解可對(duì)java.util.Datejava.util.Calendar麦乞、java.long.Long時(shí)間類型進(jìn)行標(biāo)注蕴茴,比較重要的屬性:

  • pattern屬性:類型為字符串,指定解析/格式化字段數(shù)據(jù)的模式姐直,如:"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"声畏,因此當(dāng)我們輸入的日期為"yyyy-MM-dd"時(shí)都能給解析并轉(zhuǎn)換成Date撞叽。

數(shù)值格式化

@NumberFormat可對(duì)類似數(shù)字類型的屬性進(jìn)行標(biāo)注,它擁有兩個(gè)互斥的屬性:

  • style
    類型為Numberformat.Style插龄。用于指定樣式類型愿棋,包括三種:Style.NUMBER(正常數(shù)字類型)、Style.CURRENCY(貨幣類型)均牢、Style.PERCENT(百分?jǐn)?shù)類型)糠雨。
  • 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 +
                '}';
    }
}

上面的代碼標(biāo)識(shí)了@NumberFormat(pattern = "#,###,###.#")后,例如輸入1,234,567.8垮庐,就能自動(dòng)轉(zhuǎn)成數(shù)字松邪。


數(shù)據(jù)校驗(yàn)

JSR303

JSR 303是Java為Bean數(shù)據(jù)合法性校驗(yàn)提供的標(biāo)準(zhǔn)框架,它已經(jīng)包含在JavaEE6.0中突硝,JSR 303通過(guò)在Bean屬性上標(biāo)注類似于@NotNull测摔、@Max等標(biāo)準(zhǔn)的注解指定校驗(yàn)規(guī)則,并通過(guò)標(biāo)準(zhǔn)的驗(yàn)證接口對(duì)Bean進(jìn)行驗(yàn)證。

注解 功能說(shuō)明
@Null 被注釋的元素必須為null
@NotNull 被注釋的元素必須不為null
@AssertTure 被注釋的元素必須為true
@AssertFalse 被注釋的元素必須為false
@Min(value) 被注釋的元素必須是一個(gè)數(shù)字锋八,其值必須大于等于指定的最小值
@Max(value) 被注釋的元素必須是一個(gè)數(shù)字浙于,其值必須小于等于指定的最大值
@DecimalMin(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須大于等于指定的最小值
@DecimalMax(value) 被注釋的元素必須是一個(gè)數(shù)字挟纱,其值必須小于等于指定的最大值
@Size(max,min) 被注釋的元素必須在指定的范圍內(nèi)
@Digits(integer,fraction) 被注釋的元素必須是一個(gè)數(shù)字羞酗,其值必須在可接受的范圍內(nèi)
@Past 被注釋的元素必須是一個(gè)過(guò)去的日期
@Future 被注釋的元素必須是一個(gè)將來(lái)的日期
@Pattern(value) 被注釋的元素必須符合指定的正則表達(dá)式

Hibernate Validator擴(kuò)展注解

Hibernate Validator是JSR 303的一個(gè)參考實(shí)現(xiàn),除支持所有標(biāo)準(zhǔn)的校驗(yàn)注解外紊服,它還支持以下的擴(kuò)展注解

注解 功能說(shuō)明
@Email 被注釋的元素必須是電子郵箱地址
@Length 被注釋的字符串的大小必須在指定的范圍內(nèi)
@NotEmpty 被注釋的字符串必須非空
@Range 被注釋的元素必須在合適的范圍內(nèi)

SpringMVC數(shù)據(jù)校驗(yàn)

Spring4.0擁有自己獨(dú)立的數(shù)據(jù)校驗(yàn)框架檀轨,同時(shí)支持JSR 303標(biāo)準(zhǔn)的校驗(yàn)框架。Spring在進(jìn)行數(shù)據(jù)綁定時(shí)欺嗤,可同時(shí)調(diào)用校驗(yàn)框架完成數(shù)據(jù)校驗(yàn)工作参萄。在SpringMVC中,可直接通過(guò)注解驅(qū)動(dòng)的方式進(jìn)行數(shù)據(jù)校驗(yàn)煎饼。

Spring的LocalValidatorFactoryBean既實(shí)現(xiàn)了Spring的Validator接口讹挎,也實(shí)現(xiàn)了JSR 303的Validator接口,只要在Spring容器中定義一個(gè)LocalValidatorFactoryBean吆玖,即可將其注入到需要數(shù)據(jù)校驗(yàn)的Bean中筒溃。

Spring本身并沒(méi)有提供JSR 303的實(shí)現(xiàn),所以必須將JSR 303的jar包放到類路徑下沾乘。

<mvc:annotation-driven />會(huì)默認(rèn)裝配好一個(gè)LocalValidatorFactroyBean怜奖,通過(guò)在處理方法的入?yún)⑸蠘?biāo)注@valid注解即可讓SpringMVC在完成數(shù)據(jù)綁定后執(zhí)行數(shù)據(jù)校驗(yàn)的工作。

在已經(jīng)標(biāo)注了JSR 303注解的表單/命令對(duì)象前標(biāo)注一個(gè)@Valid翅阵,SpringMVC框架在將請(qǐng)求參數(shù)綁定到該入?yún)?duì)象后歪玲,就會(huì)調(diào)用校驗(yàn)框架根據(jù)注解聲明的校驗(yàn)規(guī)則實(shí)施校驗(yàn)。

SpringMVC是通過(guò)對(duì)處理方法簽約的規(guī)約來(lái)保存校驗(yàn)結(jié)果的:前一個(gè)表單/命令對(duì)象的校驗(yàn)結(jié)果保存到隨后的入?yún)⒅性豕耍@個(gè)保存校驗(yàn)結(jié)果的入?yún)⒈仨毷?code>BindingResult或Errors類型读慎,這兩個(gè)類都位于org.springframework.validation包中。要注意的一點(diǎn)是:需要校驗(yàn)的Bean對(duì)象和其綁定結(jié)果對(duì)象或錯(cuò)誤對(duì)象是成對(duì)出現(xiàn)的槐雾,它們之間不允許聲明其他的入?yún)?/strong>
例如下面的代碼是錯(cuò)誤的:

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

Employee和BindingResult兩個(gè)參數(shù)應(yīng)該是在一起的夭委,例如下面的才是正確的:

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

Errors接口提供了獲取錯(cuò)誤信息的方法,如getErrorCount()getFieldErrors(String field)募强,BindingResult擴(kuò)展了Errors接口株灸。

@RequestMapping(value = "/emp",method = RequestMethod.POST)
    public String save(@Valid Employee employee, BindingResult result,Map<String,Object> map){
        System.out.println("save:"+employee);
        /**
         * 如果轉(zhuǎn)換出錯(cuò)
         */
        if (result.getErrorCount() > 0){
            System.out.println("出錯(cuò)了:");
            for (FieldError error : result.getFieldErrors()){
                System.out.println(error.getField() + ":" + error.getDefaultMessage());
            }
            map.put("departments",departmentDao.getDepartments());
            
            //若驗(yàn)證出錯(cuò),則轉(zhuǎn)向定制的頁(yè)面
            return "input";
        }
        employeeDao.save(employee);
        return "redirect:/emps";
    }

SpringMVC整合擴(kuò)展的Hibernate Validator驗(yàn)證框架數(shù)據(jù)校驗(yàn)的步驟

  • 使用JSR 303驗(yàn)證標(biāo)準(zhǔn)完成數(shù)據(jù)校驗(yàn)擎值。
  • 加入hibernate validator驗(yàn)證框架的jar包慌烧。
  • 在SpringMVC配置文件中添加<mvc:annotation-dirven />
  • 需要在bean的屬性上添加對(duì)應(yīng)的注解
  • 在目標(biāo)方法bean類型的前面添加@Valid注解

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


然后在配置文件中添加<mvc:annotation-dirven />鸠儿,因?yàn)樵撆渲迷谏厦骖愋娃D(zhuǎn)換時(shí)定義了一個(gè)自定義的類型轉(zhuǎn)換器屹蚊,因此配置了FormattingConversionServiceFactoryBean厕氨,如果不需要使用自定義類型轉(zhuǎn)換器的話,這部分代碼可以去掉:

<?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">
    <!-- 配置自動(dòng)掃描的包-->
    <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>

對(duì)上面的Employee類汹粤,我們可以做一些驗(yàn)證如下命斧,添加@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 +
                '}';
    }
}

并在處理的方法中的入?yún)⒅袑?duì)要校驗(yàn)的實(shí)體類標(biāo)注@Valid国葬,如果需要使用到BindingResult等類的話要注意要和@Valid標(biāo)注的實(shí)體類成對(duì)存在:

@RequestMapping(value = "/emp",method = RequestMethod.POST)
    public String save(@Valid Employee employee, BindingResult result,Map<String,Object> map){
        System.out.println("save:"+employee);
        /**
         * 如果轉(zhuǎn)換出錯(cuò)
         */
        if (result.getErrorCount() > 0){
            System.out.println("出錯(cuò)了:");
            for (FieldError error : result.getFieldErrors()){
                System.out.println(error.getField() + ":" + error.getDefaultMessage());
            }
            map.put("departments",departmentDao.getDepartments());
            
            //若驗(yàn)證出錯(cuò),則轉(zhuǎn)向定制的頁(yè)面
            return "input";
        }
        employeeDao.save(employee);
        return "redirect:/emps";
    }

如果輸入錯(cuò)誤芹壕,則輸出結(jié)果如下:


在頁(yè)面上顯示錯(cuò)誤

在JSP頁(yè)面上通過(guò)<form:errors path="屬性名" />顯示錯(cuò)誤消息

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

如果要一次性顯示所有錯(cuò)誤消息汇四,可以將path寫(xiě)為*,即:

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

提示消息的國(guó)際化

每個(gè)屬性在數(shù)據(jù)綁定和數(shù)據(jù)校驗(yàn)發(fā)生錯(cuò)誤時(shí)踢涌,都會(huì)生成一個(gè)對(duì)應(yīng)的FieldError對(duì)象通孽。當(dāng)一個(gè)屬性校驗(yàn)失敗后,校驗(yàn)框架會(huì)為該屬性生成4個(gè)消息代碼睁壁,這些代碼以校驗(yàn)注解類名為前綴利虫,結(jié)合modleAttribute、屬性名及屬性類型名生成多個(gè)對(duì)應(yīng)的消息代碼堡僻。
例如User類中的password屬性標(biāo)注了一個(gè)@Pattern注解,當(dāng)該屬性值不滿足@Pattern所定義的規(guī)則時(shí)疫剃,就會(huì)產(chǎn)生以下4個(gè)錯(cuò)誤代碼:

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

當(dāng)使用SpringMVC標(biāo)簽顯示錯(cuò)誤消息時(shí)钉疫,SpringMVC會(huì)查看WEB上下文是否裝配了對(duì)應(yīng)的國(guó)際化消息,如果沒(méi)有巢价,則顯示默認(rèn)的錯(cuò)誤消息牲阁,否則使用國(guó)際化消息。

定制提示消息的話壤躲,我們要先定義一個(gè)國(guó)際化資源文件城菊,例如i18n.properties文件,并且寫(xiě)入需要定制化的消息:

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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末碉克,一起剝皮案震驚了整個(gè)濱河市凌唬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌漏麦,老刑警劉巖客税,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異撕贞,居然都是意外死亡更耻,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)捏膨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)秧均,“玉大人食侮,你說(shuō)我怎么就攤上這事∧亢” “怎么了锯七?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)讶隐。 經(jīng)常有香客問(wèn)我起胰,道長(zhǎng),這世上最難降的妖魔是什么巫延? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任效五,我火速辦了婚禮,結(jié)果婚禮上炉峰,老公的妹妹穿的比我還像新娘畏妖。我一直安慰自己,他們只是感情好疼阔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布戒劫。 她就那樣靜靜地躺著,像睡著了一般婆廊。 火紅的嫁衣襯著肌膚如雪迅细。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,274評(píng)論 1 300
  • 那天淘邻,我揣著相機(jī)與錄音茵典,去河邊找鬼。 笑死宾舅,一個(gè)胖子當(dāng)著我的面吹牛统阿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播筹我,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼扶平,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蔬蕊?” 一聲冷哼從身側(cè)響起结澄,我...
    開(kāi)封第一講書(shū)人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎岸夯,沒(méi)想到半個(gè)月后概而,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡囱修,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年赎瑰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片破镰。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡餐曼,死狀恐怖压储,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情源譬,我是刑警寧澤集惋,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站踩娘,受9級(jí)特大地震影響刮刑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜养渴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一雷绢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧理卑,春花似錦翘紊、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至宇立,卻和暖如春踪宠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背妈嘹。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工殴蓬, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蟋滴。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像痘绎,于是被迫代替她去往敵國(guó)和親津函。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容