内容关键词:转换、格式化、验证器
知识来源: 720科技(张森鹏)
一、知识笔记
Spring 的 Converter 是可以将一种类型转换成另一种类型的一个对象。 例如,用户输入的日期可能有许多种形式,如“December 25, 2014”“12/25/2014”和“2014-12-25”,这些都表示同一个日期。默认情况下, Spring 会期待用户输入的日期样式与当前语言区域的日期样式相同。例如,对于美国的用户而言,就是月/日/年格式。如果希望 Spring 在将输入的日期字符串绑定到 LocalDate 时,使用不同的日期样式,则需要编写一个 Converter,才能将字符串转换成日期。 java.time.LocalDate 类是 Java 8 的一个新类型,用来替代 java.util.Date。 还需使用新的 Date/Time API 来替换旧有的 Date 和 Calendar 类。
Formatter 就像 Converter 一样,也是将一种类型转换成另一种类型。但是, Formatter 的源类型必须是一个 String,而 Converter 则适用于任意的源类型。 Formatter 更适合 Web 层,而Converter 则可以用在任意层中。为了转换 Spring MVC 应用程序表单中的用户输入,始终应该选择 Formatter,而不是 Converter。
二、重要知识
1、Converter
为了创建 Converter,必须编写实现 org.springframework.core.convert.converter. Converter接口的一个 Java 类。这个接口的声明如下:
public interface Converter<S, T>
这里的 S 表示源类型, T 表示目标类型。例如,为了创建一个可以将 Long 转换成 Date的 Converter,要像下面这样声明 Converter 类:
}
在类实体中,需要编写一个来自 Converter 接口的 convert 方法实现。这个方法的签名
如下:
T convert(S source)
例如,清单 6.1 展示了一个适用于任意日期样式的 Converter。
清单 6.1 String To LocalDate Converter
package converter;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import org.springframework.core.convert.converter.Converter;
public class StringToLocalDateConverter implements Converter<String,
LocalDate> {
private String datePattern;
public StringToLocalDateConverter(String datePattern) {
this.datePattern = datePattern;
}
@Override
public LocalDate convert(String s) {
try {
return LocalDate.parse(s, DateTimeFormatter.ofPattern(
datePattern));
} catch (DateTimeParseException e) {
// the error message will be displayed in <form:errors>
throw new IllegalArgumentException(
"invalid date format. Please use this pattern\""
+ datePattern + "\"");
}
}
}
注意清单 6.1 中的 Converter 方法,它利用传给构造器的日期样式,将一个 String 转换成LocalDate。为了使用 Spring MVC 应用程序中定制的 Converter,需要 在 Spring MVC 配置文件中编写一个名为 conversionService 的 bean。 bean 的类名称必须为 org.springframework.context.support.ConversionServiceFactoryBean。这个 bean 必须包含一个 converters 属性,它将列出要在应用程序中使用的所有定制 Converter。例如,下面的 bean 声明在清单 6.1 中注册了StringToDateConverter。
<bean id="conversionService" class="org.springframework.context.support.
ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="converter.StringToLocalDateConverter">
<constructor-arg type="java.lang.String"
value="MM-dd-yyyy"/>
</bean>
</list>
</property>
</bean>
随后,要给 annotation-driven 元素的 conversion-service 属性赋值 bean 名称(本例中是conversionService),如下所示:
<mvc:annotation-driven
conversion-service="conversionService"/>
converter-demo 是一个范例应用程序,它利用 StringToLocalDateConverter 将 String 转换成Employee 对象的 birthDate 属性。 Employee 类如清单 6.2 所示。
清单 6.2 Employee 类
package domain;
import java.io.Serializable;
import java.time.LocalDate;
public class Employee implements Serializable {
private static final long serialVersionUID = -908L;
private long id;
private String firstName;
private String lastName;
private LocalDate birthDate;
private int salaryLevel;
// getters and setters not shown
}
清单 6.3 中的 EmployeeController 类是 domain 对象 Employee 的控制器。
清单 6.3 converter-demo 中的 EmployeeController 类
package controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import domain.Employee;
@Controller
public class EmployeeController {
@RequestMapping(value="/add-employee")
public String inputEmployee(Model model) {
model.addAttribute(new Employee());
return "EmployeeForm";
}
@RequestMapping(value="/save-employee")
public String saveEmployee(@ModelAttribute Employee employee,
BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
FieldError fieldError = bindingResult.getFieldError();
return "EmployeeForm";
}
// save employee here
model.addAttribute("employee", employee);
return "EmployeeDetails";
}
}
EmployeeController 类 有 inputEmployee 和 saveEmployee 两 个 处 理 请求 的方 法 。inputEmployee 方法返回清单 6.4 中的 EmployeeForm.jsp 页面。 saveEmployee 方法取出在提交mployee 表单时创建的一个 Employee 对象。有了 StringToLocalDateConverter converter,就不需要劳驾控制器类将字符串转换成日期了。
2、Formatter
为了创建 Formatter,要编写一个实现 org.springframework.format.Formatter 接口的 Java 类。下面是这个接口的声明:
public interface Formatter<T>
这里的 T 表示输入字符串要转换的目标类型。该接口有 parse 和 print 两个方法,所有实现都必须覆盖它们。
T parse(String text, java.util.Locale locale)
String print(T object, java.util.Locale locale)
parse 方法利用指定的 Locale 将一个 String 解析成目标类型。 print 方法与之相反,它返回目标对象的字符串表示法。
例如, formatter-demo 应用程序中用一个 LocalDateFormatter 将 String 转换成 Date。其作用与 converter-demo 中的 StringToLocalDateConverter 一样。
DateFormatter 类如清单 6.5 所示。
package formatter;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Locale;
import org.springframework.format.Formatter;
public class LocalDateFormatter implements Formatter<LocalDate> {
private DateTimeFormatter formatter;
private String datePattern;
public LocalDateFormatter(String datePattern) {
this.datePattern = datePattern;
formatter= DateTimeFormatter.ofPattern(datePattern);
}
@Override
public String print(LocalDate date, Locale locale) {
return date.format(formatter);
}
@Override
public LocalDate parse(String s, Locale locale)
throws ParseException {
try {
return LocalDate.parse(s,
DateTimeFormatter.ofPattern(datePattern));
} catch (DateTimeParseException e) {
// the error message will be displayed in <form:errors>
throw new IllegalArgumentException(
"invalid date format. Please use this pattern\""
+ datePattern + "\"");
}
}
}
为了在 Spring MVC 应用程序中使用 Formatter,需要利用名为 conversionService 的 bean对它进行注册。 bean 的类名称必须为 org.springframework.format.support.FormattingConversionServiceFactoryBean。这与 converter-demo 中用于注册 converter 的类不同。这个 bean 可以用一个 formatters 属性注册 formatter,用一个 converters 属性注册 converter。清单 6.6 展示了formatter-demo 的 Spring 配置文件。
清单 6.6 formatter-demo 的 Spring 配置文件
<?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:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="controller"/>
<context:component-scan base-package="formatter"/>
<mvc:annotation-driven conversion-service="conversionService"/>
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/*.html" location="/"/>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.
InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="conversionService"
class="org.springframework.format.support.
FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<bean class="formatter.LocalDateFormatter">
<constructor-arg type="java.lang.String"
value="MM-dd-yyyy" />
</bean>
</set>
</property>
</bean>
</beans>
注意,还需要给这个 Formatter 添加一个 component-scan 元素。
3、用Registrar注册Formatter
注册 Formatter 的另一种方法是使用 Registrar。例如,清单 6.7 就是注册 DateFormatter 的一个例子。
清单 6.7 MyFormatterRegistrar 类
package formatter;
import org.springframework.format.FormatterRegistrar;
import org.springframework.format.FormatterRegistry;
public class MyFormatterRegistrar implements FormatterRegistrar {
private String datePattern;
public MyFormatterRegistrar(String datePattern) {
this.datePattern = datePattern;
}
@Override
public void registerFormatters(FormatterRegistry registry) {
registry.addFormatter(new LocalDateFormatter(datePattern));
// register more formatters here
}
}
有了 Registrar,就不需要在 Spring MVC 配置文件中注册任何 Formatter 了,只在 Spring配置文件中注册 Registrar 就可以了,如清单 6.8 所示。
清单 6.8 在 springmvc-config.xml 文件中注册 Registrar
<?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:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/springcontext.xsd">
<context:component-scan base-package="controller" />
<context:component-scan base-package="formatter" />
<mvc:annotation-driven conversion-service="conversionService" />
<mvc:resources mapping="/css/**" location="/css/" />
<mvc:resources mapping="/*.html" location="/" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.
InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="conversionService"
class="org.springframework.format.support.
FormattingConversionServiceFactoryBean">
<property name="formatterRegistrars">
<set>
<bean class="formatter.MyFormatterRegistrar">
<constructor-arg type="java.lang.String"
value="MM-dd-yyyy" />
</bean>
</set>
</property>
</bean>
</beans>
4、选择 Converter, 还是 Formatter
Converter 是一般工具,可以将一种类型转换成另一种类型。例如,将 String 转换成LocalDate,或者将 Long 转换成 LocalDate。 Converter 既可以用在 Web 层,也可以用在其他层中。
Formatter 只能将 String 转换成另一种 Java 类型。例如,将 String 转换成 LocalDate,但它不能将 Long 转换成 LocalDate。因此, Formatter 适用于 Web 层。为此,在 Spring MVC 应用程序中,选择 Formatter 比选择 Converter 更合适。
5、Spring 的 Validator 范例
spring-validator 应用程序中包含一个名为 ProductValidator 的验证器,用于验证 Product 对
象。 Spring-validator 的 Product 类如清单 7.2 所示。 ProductValidator 类如清单 7.3 所示。清单
a、 Product 类
package domain;import java.io.Serializable;
import java.math,BigDecimal;
import java.time.LocalDate;
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String description;
private BigDecimal price;
private LocalDate productionDate;
//getters and setters methods not shown
}
b、 ProductValidator 类
package validator;
import java.math,BigDecimal;
import java.time.LocalDate;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import domain.Product;
public class ProductValidator implements Validator {
@Override
public boolean supports(Class<?> klass) {
return Product.class.isAssignableFrom(klass);
}
@Override
public void validate(Object target, Errors errors) {
Product product = (Product) target;
ValidationUtils.rejectIfEmpty(errors, "name",
"productname.required");
ValidationUtils.rejectIfEmpty(errors, "price","price.required");
ValidationUtils.rejectIfEmpty(errors, "productionDate",
"productiondate.required");
BigDecimal price = product.getPrice();
if (price != null && price.compareTo(BigDecimal.ZERO) < 0) {
errors.rejectValue("price", "price.negative");
}
Local Date productionDate = product.getProductionDate();
if (productionDate != null) {
if (productionDate.isAfter(LocalDate.now())) {
errors.rejectValue("productionDate",
"productiondate.invalid");
}
}
}
}
ProductValidator 验证器是一个非常简单的验证器。它的 validate 方法会检验 Product 是否
有名称和价格,并且价格是否不为负数。它还会确保生产日期不晚于今天。
三、学习参考
https://m.2cto.com/kf/201707/654979.html
https://blog.csdn.net/slowly_come_faster
SpringMVC学习指南