數(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ò)ConversionServiceFactoryBean
的converters
屬性注冊(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è)RequestMappingHandlerMapping
、RequestMappingHandlerAdapter
與ExceptionHandlerExceptionResolver
三個(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.Date
、java.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