javascript
Spring MVC-06循序渐进之Converter和Formatter
- 概述
- converter
- Step 1 實現Converter接口
- Step 2 SpringMVC配置文件中配置bean及設置conversion-service屬性
- 小Demo
- formatter
- Step 1 實現Formatter接口
- Step 2 SpringMVC配置文件中配置bean及設置conversion-service屬性
- 用registrar注冊formatter
- Step 1 編寫MyFormatterRegistrar ,注冊自定義Formatter
- Step 2 編寫自定義Formatter
- Step 3 SpringMVC配置文件中配置bean及設置conversion-service屬性
- converter or formatter 小結
- 源碼
概述
Spring MVC-05循序漸進之數據綁定和form標簽庫(上) 和 Spring MVC-05循序漸進之數據綁定和form標簽庫(下) 實戰從0到1 我們已經學習了數據綁定,見識了數據綁定的方便性。
但是Spring的數據綁定并非沒有任何限制, 比如Spring總是試圖使用more的語言區域將日期輸入綁定到java.uti.Date上,假設我們想讓Spring使用不同的格式日期,就需要一個Converter或者Formatter來協助完成了。
本篇博文將重點討論Converter和Formatter的內容。 這兩者均可以用于將一種對象類型轉換成另外一種對象類型。 Converter是通用元件,可以在應用程序的任意層使用,而Formatter則是專門為Web層設計的
converter
Spring 的Converter是可以將一種類型轉換成另外一種類型的一個對象。
舉個例子,用戶輸入的日期格式可能有許多種,比如“January 10,2018”、“10/01/2018”、“2018-01-10”,這些都表示同一個日期。 默認情況下,Spring會將期待用戶輸入的日期樣式和當前語言區域的日期樣式相同。
比如US用戶,就是月/日/年的格式。 如果希望Spring在將輸入的日期字符串綁定到Date時使用不同的日期格式,則需要編寫一個Converter,才能將字符串轉換成日期。
Step 1 實現Converter接口
為了創建自定義的Converter需要實現 org.springframework.core.convert.converter.Converter接口
我們來看下該接口
package org.springframework.core.convert.converter;/*** A converter converts a source object of type {@code S} to a target of type {@code T}.** <p>Implementations of this interface are thread-safe and can be shared.** <p>Implementations may additionally implement {@link ConditionalConverter}.** @author Keith Donald* @since 3.0* @param <S> the source type* @param <T> the target type*/ public interface Converter<S, T> {/*** Convert the source object of type {@code S} to target type {@code T}.* @param source the source object to convert, which must be an instance of {@code S} (never {@code null})* @return the converted object, which must be an instance of {@code T} (potentially {@code null})* @throws IllegalArgumentException if the source cannot be converted to the desired target type*/T convert(S source);}這里的泛型 S表示源類型, 泛型T表示目標類型。 比如為了創建一個可以將String轉為Date的Converter,可以像下面這樣聲明
public class MyConverter implements Converter<String, Date> {}當然了要重寫convert方法。
@Overridepublic Date convert(String source){}該例中的代碼如下
MyConverter.java
package com.artisan.converter;import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date;import org.springframework.core.convert.converter.Converter; /*** * @ClassName: MyConverter * @Description: 自定義Converter,將String類型的數據轉換為指定格式的Date * @author Mr.Yang * @date 2018年2月10日 **/ public class MyConverter implements Converter<String, Date> {private String datePattern;private Date targetFormateDate;/*** * 創建一個新的實例 MyConverter. 默認構造函數**/public MyConverter() {super();}/*** * 創建一個新的實例 MyConverter. 實例化時指定日期格式* * @param datePattern*/public MyConverter(String datePattern) {super();this.datePattern = datePattern;}/*** 重寫convert方法*/@Overridepublic Date convert(String source) {try {SimpleDateFormat sdf = new SimpleDateFormat(datePattern);// 是否嚴格解析日期,設置false禁止SimpleDateFormat的自動計算功能sdf.setLenient(false);targetFormateDate = sdf.parse(source);} catch (ParseException e) {// the error message will be displayed when using <form:errors>e.printStackTrace();throw new IllegalArgumentException("invalid date format. Please use this pattern\"" + datePattern + "\"");}return targetFormateDate;}}/*** 如果設置為true,假設你輸入的日期不合法,它會先進行一定的計算.計算出能有合法的值,就以計算后的值為真正的值.* * 比如說當你使用的時候有2012-02-31,2012-14-03這樣數據去format,* 如果setLenient(true).那么它就會自動解析為2012-03-02和2013-02-03這樣的日期.* 如果setLenient(false),2012-14-03就會出現解析異常,因為去掉了計算,而這樣的數據又是不合法的* */Step 2 SpringMVC配置文件中配置bean及設置conversion-service屬性
為了在Spring MVC中使用自定義的Converter,需要在SpringMVC的配置文件中配置一個conversionService ,該Bean的名字必須為
org.springframework.context.support.ConversionServiceFactoryBean 。同時必須包含一個converters屬性,它將列出要在應用程序中使用的所有定制的Converter.
緊接著要給annotation-driven元素的conversion-service屬性賦bean名稱。
完整配置文件如下:
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 掃描控制層的注解,使其成為Spring管理的Bean --><context:component-scan base-package="com.artisan.controller"/><!-- 靜態資源文件 --><!-- (2)將自定義的convert設置給conversion-service屬性 --><mvc:annotation-driven conversion-service="conversionService"/><mvc:resources mapping="/css/**" location="/css/"/><mvc:resources mapping="/*.jsp" location="/"/><!-- 視圖解析器 --><bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/jsp/"/><property name="suffix" value=".jsp"/></bean><!-- (1)自定義converter --><bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"><property name="converters"><list><bean class="com.artisan.converter.MyConverter"><constructor-arg type="java.lang.String" value="MM-dd-yyyy"/></bean></list></property></bean></beans>小Demo
Domain類
package com.artisan.domain;import java.io.Serializable; import java.util.Date;public class Artisan implements Serializable {private static final long serialVersionUID = -908L;private long id;private String firstName;private String lastName;private Date birthDate;private int salaryLevel;public long getId() {return id;}public void setId(long id) {this.id = id;}public String getFirstName() {return firstName;}public void setFirstName(String firstName) {this.firstName = firstName;}public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName = lastName;}public Date getBirthDate() {return birthDate;}public void setBirthDate(Date birthDate) {this.birthDate = birthDate;}public int getSalaryLevel() {return salaryLevel;}public void setSalaryLevel(int salaryLevel) {this.salaryLevel = salaryLevel;}}Controller
package com.artisan.controller;import org.apache.log4j.Logger; 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 com.artisan.domain.Artisan;/*** * @ClassName: ArtisanController * @Description: @Controller標注的Artisan控制層 * @author Mr.Yang * @date 2018年2月10日 **/ @Controller public class ArtisanController {private Logger logger = Logger.getLogger(ArtisanController.class);@RequestMapping(value="/artisan_input")public String inputArtisan(Model model) {model.addAttribute(new Artisan());return "ArtisanForm";}@RequestMapping(value="/artisan_save")public String saveArtisan(@ModelAttribute Artisan artisan, BindingResult bindingResult,Model model) {// 如果輸入錯誤,跳轉到ArtisanForm頁面if (bindingResult.hasErrors()) {FieldError fieldError = bindingResult.getFieldError();logger.info("Code:" + fieldError.getCode() + ", field:" + fieldError.getField());return "ArtisanForm";}// save Artisan heremodel.addAttribute("artisan", artisan);return "ArtisanDetails";} }前臺JSP
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE HTML> <html> <head> <title>Add Artisan Form</title> <style type="text/css">@import url("<c:url value="/css/main.css"/>");</style> </head> <body><div id="global"> <form:form commandName="artisan" action="artisan_save" method="post"><fieldset><legend>Add an Artisan</legend><p><label for="firstName">First Name: </label><form:input path="firstName" tabindex="1"/></p><p><label for="lastName">Last Name: </label><form:input path="lastName" tabindex="2"/></p><p><!-- 接收顯示錯誤信息 --><form:errors path="birthDate" cssClass="error"/></p><p><label for="birthDate">Date Of Birth: </label><form:input path="birthDate" tabindex="3" /></p><p id="buttons"><input id="reset" type="reset" tabindex="4"><input id="submit" type="submit" tabindex="5" value="Add Artisan"></p></fieldset> </form:form> </div> </body> </html>展示頁面
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE HTML> <html> <head> <title>Save Artisan</title> <style type="text/css">@import url("<c:url value="/css/main.css"/>");</style> </head> <body> <div id="global"><h4>The Artisan details have been saved.</h4><p><h5>Details:</h5>First Name: ${artisan.firstName}<br/>Last Name: ${artisan.lastName}<br/>Date of Birth: ${artisan.birthDate}</p> </div> </body> </html>formatter
Formatter就像Converter一樣,也是將一種類型轉換為另外一種類型,但是Formatter的源類型必須是String,而Converter的源類型可以是任意類型。
Formatter更加適合Web層,而Converter則可以在任意層中。
為了轉換SpringMVC應用程序中的表單的用戶輸入,始終應該選擇Formatter而不是Converter
Step 1 實現Formatter接口
我們先看下
org.springframework.format.Formatter的源碼
可以知道需要重寫兩個方法
parse方法利用指定Locale將一個String解析為目標類型
print方法與之相反,它是返回目標對象的字符串表示法
來看下具體的用法
編寫自定義Formatter類
package com.artisan.converter;import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale;import org.springframework.format.Formatter;public class MyFormatter implements Formatter<Date> {private String datePattern;private SimpleDateFormat dateFormat;public MyFormatter(String datePattern) {this.datePattern = datePattern;dateFormat = new SimpleDateFormat(datePattern);dateFormat.setLenient(false);}@Overridepublic String print(Date date, Locale locale) {return dateFormat.format(date);}@Overridepublic Date parse(String s, Locale locale) throws ParseException {try {return dateFormat.parse(s);} catch (ParseException e) {e.printStackTrace();// the error message will be displayed when using <form:errors>throw new IllegalArgumentException("invalid date format. Please use this pattern\"" + datePattern + "\"");}}}Step 2 SpringMVC配置文件中配置bean及設置conversion-service屬性
注冊配置文件
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 掃描控制層的注解,使其成為Spring管理的Bean --><context:component-scan base-package="com.artisan.controller"/><!-- 靜態資源文件 --><!-- (2)將自定義的convert設置給conversion-service屬性 --><mvc:annotation-driven conversion-service="conversionService"/><mvc:resources mapping="/css/**" location="/css/"/><mvc:resources mapping="/*.jsp" location="/"/><!-- 視圖解析器 --><bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/jsp/"/><property name="suffix" value=".jsp"/></bean><!-- (1)自定義fromatter --><bean id="conversionService"class="org.springframework.format.support.FormattingConversionServiceFactoryBean"><property name="formatters"><set><bean class="com.artisan.converter.MyFormatter"><constructor-arg type="java.lang.String" value="MM-dd-yyyy" /></bean></set></property></bean></beans>
為了在Spring中使用Formatter,需要注冊org.springframework.format.support.FormattingConversionServiceFactoryBean這個bean。
這個bean與converter的
org.springframework.context.support.ConversionServiceFactoryBean 不同,FormattingConversionServiceFactoryBean可以用一個formatters屬性注冊Formatter,也可以用converters屬性注冊converter
測試結果同上。
用registrar注冊formatter
注冊Formatter的另外一種方式是使用Registrar.
Step 1 編寫MyFormatterRegistrar ,注冊自定義Formatter
package com.artisan.converter;import org.springframework.format.FormatterRegistrar; import org.springframework.format.FormatterRegistry;public class MyFormatterRegistrar implements FormatterRegistrar {private String dataPattern;public MyFormatterRegistrar(String dataPattern) {super();this.dataPattern = dataPattern;}@Overridepublic void registerFormatters(FormatterRegistry registry) {registry.addFormatter(new MyFormatter(dataPattern));// u can registry more formatters here }}Step 2 編寫自定義Formatter
package com.artisan.converter;import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale;import org.springframework.format.Formatter;public class MyFormatter implements Formatter<Date> {private String datePattern;private SimpleDateFormat dateFormat;public MyFormatter(String datePattern) {this.datePattern = datePattern;dateFormat = new SimpleDateFormat(datePattern);dateFormat.setLenient(false);}@Overridepublic String print(Date date, Locale locale) {return dateFormat.format(date);}@Overridepublic Date parse(String s, Locale locale) throws ParseException {try {return dateFormat.parse(s);} catch (ParseException e) {e.printStackTrace();// the error message will be displayed when using <form:errors>throw new IllegalArgumentException("invalid date format. Please use this pattern\"" + datePattern + "\"");}}}修改SpringMVC配置文件
有了Registrar,就不用再SpringMVC配置文件中注冊任何Formatter了,只要在SpringMVC配置文件中注冊Registrar就可以了
Step 3 SpringMVC配置文件中配置bean及設置conversion-service屬性
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 掃描控制層的注解,使其成為Spring管理的Bean --><context:component-scan base-package="com.artisan.controller"/><!-- 靜態資源文件 --><!-- (2)將自定義的convert設置給conversion-service屬性 --><mvc:annotation-driven conversion-service="conversionService"/><mvc:resources mapping="/css/**" location="/css/"/><mvc:resources mapping="/*.jsp" location="/"/><!-- 視圖解析器 --><bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/jsp/"/><property name="suffix" value=".jsp"/></bean><!-- (1)自定義fromatter --><bean id="conversionService"class="org.springframework.format.support.FormattingConversionServiceFactoryBean"><property name="formatterRegistrars"><set><bean class="com.artisan.converter.MyFormatterRegistrar"><constructor-arg type="java.lang.String" value="MM-dd-yyyy" /></bean></set></property></bean></beans>測試結果同上。
converter or formatter 小結
Converter 是一般工具 ,可以將將一種類型轉換為另外一種類型,比如將String 轉為Date , Long 轉為Date .既可以適應在Web層,也可以使用在其他層中。
Formatter的源類型必須是String,比如將String 轉為Date , 但是不能將Long 轉為Date
**為了轉換SpringMVC應用程序中的表單的用戶輸入,始終應該選擇Formatter而不是Converter**Formatter更加使用Web層。
源碼
代碼已提交到github
https://github.com/yangshangwei/SpringMvcTutorialArtisan
總結
以上是生活随笔為你收集整理的Spring MVC-06循序渐进之Converter和Formatter的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring MVC-04循序渐进之基于
- 下一篇: Spring MVC-05循序渐进之数据