Spring MVC的數(shù)據(jù)轉(zhuǎn)換及數(shù)據(jù)格式化

Spring MVC會根據(jù)請求方法簽名不同,將請求消息中信息以一定方式轉(zhuǎn)換并綁定到請求方法的參數(shù)中。

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

Spring MVC通過反射機制對目標(biāo)處理方法的簽名進行分析,并將請求消息綁定到處理方法的參數(shù)上。數(shù)據(jù)綁定的核心部件是Databinder

Spring MVC數(shù)據(jù)綁定機制
  • 1.Spring MVC框架將ServletRequest對象及處理方法的參數(shù)實例傳遞給DataBinder。

  • 2.DataBinder調(diào)用裝配在Spring Web上下文中的ConversionService組件進行數(shù)據(jù)類型轉(zhuǎn)換、
    數(shù)據(jù)格式化工作,并將ServletRequest中的消息填充到參數(shù)對象中。

  • 3.然后再調(diào)用Validator組件對已經(jīng)綁定的請求消息數(shù)據(jù)的參數(shù)對象進行數(shù)據(jù)合法性校驗。

  • 4.最終生成數(shù)據(jù)綁定結(jié)果BindingResult對象,BindingResult包含已完成數(shù)據(jù)綁定的參數(shù)對象,還包含相應(yīng)的校驗錯誤的對象。

  • 5.Spring MVC抽取BindingResult中的參數(shù)對象及校驗對象,將它們賦給處理方法的相應(yīng)參數(shù)。

2.數(shù)據(jù)轉(zhuǎn)換(ConversionService)

在Java語言中,在java.beans包中提供了一個PropertyEditor接口來進行數(shù)據(jù)轉(zhuǎn)換(只能用于字符串和Java對象的轉(zhuǎn)換)。其功能就是將一個字符串轉(zhuǎn)換為一個Java對象。

Spring 3.0,添加了一個通用的類型轉(zhuǎn)換模塊,位于org.springframework.core.convert包中。

2.1 ConversionServiceboolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType)

org.springframework.core.convert.ConversionService是Spring類型轉(zhuǎn)換的核心接口。

  • boolean canConvert(Class<?> sourceType, Class<?> targetType)

判斷是否可以將一個Java類轉(zhuǎn)換為另一個Java類

  • boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType)

TypeDescriptor不但描述了需要轉(zhuǎn)換類的信息,還描述了類的上下文信息。這樣可以利用這些信息做出更多的各種靈活的控制。

  • <T> T convert(Object source, Class<T> targetType)

將源類型對象轉(zhuǎn)換為目標(biāo)類型對象

  • Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType)

將源類型從源類型對象轉(zhuǎn)換為目標(biāo)類型對象,通常利用到類中的上下文信息

可以利用org.springframework.context.support.ConversionServiceFactoryBean在Spring的上下文中定義一個ConversionService。Spring將自動識別出上下文種的ConversionService,并在Spring MVC處理方法的參數(shù)綁定中使用它進行數(shù)據(jù)轉(zhuǎn)換。

<bean class="org.springframework.context.support.ConversionServiceFactoryBean"/>

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

Spring 在org.springframework.core.convert.converter包中定義了3種類型的轉(zhuǎn)換器接口,我們可以實現(xiàn)其中任意一種轉(zhuǎn)換接口,并將它作為自定義轉(zhuǎn)換器注冊到ConversionServiceFactoryBean當(dāng)中。

  • Converter<S, T>

Spring中最簡單的一個轉(zhuǎn)換器接口。該方法負責(zé)將S類型轉(zhuǎn)換為T類型的對象。

  • ConverterFactory<S, R>

如果希望將一種類型的對象轉(zhuǎn)換為另一種類型及其子類對象,比如將String類型轉(zhuǎn)換為Number以及Number的子類Integer、Double等對象。就需要一系列的Converter。該接口的作用就是將這一系列的相同的Converter封裝在一起。

  • GenericConverter

Converter<S,T>只是負責(zé)將一個類型的對象轉(zhuǎn)換為另一個類型的對象,它并沒有考慮類型對象的上下文信息。因此不能完成復(fù)雜類型的轉(zhuǎn)換工作。而該接口會根據(jù)源類型的對象及其上下文進行類型轉(zhuǎn)換。

Code

public class User implements Serializable {
    private String loginname;

    private Date birthday;

    public String getLoginname() {
        return loginname;
    }

    public void setLoginname(String loginname) {
        this.loginname = loginname;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Sign Up</title>
</head>
<body>
<form action="/user/register" method="post">
    <table>
        <tr>
            <td><label>登錄名:</label></td>
            <td><input type="text" id="loginname" name="loginname"></td>
        </tr>
        <tr>
            <td><label>生日:</label></td>
            <td><input type="text" id="birthday" name="birthday"></td>
        </tr>
        <tr>
            <td>
                <input type="submit" id="submit" value="登錄">
            </td>
        </tr>
    </table>
</form>
</body>
</html>
@RequestMapping(value = "register", method = RequestMethod.POST)
public String register(@ModelAttribute User user, Model model) {
    model.addAttribute("user", user);
    return "success";
}

這時候,前臺輸入的生日為String格式的。而User實體定義的是Date時間類型的。那么后臺再接收的時候,就會報錯。

這時候,我們就自定義類型轉(zhuǎn)換器。實現(xiàn)ConversionService里面的最簡單的Converter<S,T>

public class StringToDateConverter implements Converter<String, Date> {

    /**
     * 日期類型模板,如yyyy-MM-dd
     */
    private String datePattern;

    public void setDatePattern(String datePattern) {
        this.datePattern = datePattern;
    }

    @Override
    public Date convert(String date) {
        Date result = null;
        try {
            SimpleDateFormat dateFormat = new SimpleDateFormat(this.datePattern);
            result = dateFormat.parse(date);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return result;
    }
}
  • 定義xml節(jié)點
<!--裝配自定義的類型轉(zhuǎn)換器-->
<mvc:annotation-driven conversion-service="conversionService"/>

<!--自定義Date類型轉(zhuǎn)換器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <bean class="utils.StringToDateConverter" p:datePattern="yyyy-MM-dd"/>
    </property>
</bean>

注冊方法

  • 1.InitBinder(不推薦使用java.beans.PropertyEditor)

剛才上面的注冊方式是通過xml配置進行的操作,那么我們可以不借助xml配置,使用@InitBinder添加自定義編輯轉(zhuǎn)換數(shù)據(jù)。這里就用到了Java自身的PropertyEditor類。

  • 自定義方法實現(xiàn)PropertyEditor的相關(guān)類
public class DateEditor extends PropertyEditorSupport {

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        try {
            Date date = dateFormat.parse(text);
            setValue(date);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}
  • 在控制器處進行注冊初始化
@InitBinder
public void initBinder(WebDataBinder binder){
    binder.registerCustomEditor(Date.class,new DateEditor());
}

  • 2.WebBindingInitializer(不推薦使用java.beans.PropertyEditor)

如果這個數(shù)據(jù)轉(zhuǎn)換需要在系統(tǒng)多處使用,那么這個自定義轉(zhuǎn)換器方法需要進行全局注冊。使用WebBindingInitializer進行全局范圍的注冊。

3.數(shù)據(jù)格式化(Fommatter<T>)

Spring使用Converter轉(zhuǎn)換器進行源類型對象到目標(biāo)類型對象的轉(zhuǎn)換。但是Converter并不能夠進行輸入或輸出的信息的格式化。

Spring 3.0引入格式化轉(zhuǎn)換框架,org.springframework.format,F(xiàn)ormatte<T>為最重要的接口。

Converter接口是完成任意Object與Object之間的轉(zhuǎn)換,而Formatter是完成任意Object與String的轉(zhuǎn)換。所以Formatter接口更適合在Web層,處理用戶表單提交的數(shù)據(jù)格式化。

Formatter<T>接口,完成T類型對象的格式化和解析功能。

public interface Formatter<T> extends Printer<T>, Parser<T> {

}

3.1重要接口

  • Printer<T>

格式化顯示接口

  • Parser<T>
T parse(String text, Locale locale) throws ParseException;

解析接口,參考本地信息,將一個格式化后的字符串轉(zhuǎn)換為T類型的對象。

  • Formatter<T>

Formatter<T>繼承上面兩個接口類,具備所繼承接口的所有功能。

  • FormatterRegistrar

注冊格式化轉(zhuǎn)換器。一般很少單獨用到,我們一般用到的FormattingConversionServiceFactoryBean這個里面已經(jīng)封裝了這個接口對象。

  • AnnotationFormatterFactory<A extends Annotation>

注解驅(qū)動的字段格式化工廠,用于創(chuàng)建帶注解的對象字段的Printer和Parser。即是用于格式化和解析帶注釋的對象字段。

//注解A的應(yīng)用范圍,哪些屬性類可以標(biāo)注A注解
Set<Class<?>> getFieldTypes();
//根據(jù)A注解,獲取特定屬性類型Printer
Printer<?> getPrinter(A annotation, Class<?> fieldType);
//根據(jù)A注解,獲取特定屬性類型Parser
Parser<?> getParser(A annotation, Class<?> fieldType);

3.2 自定義實現(xiàn)Formatter接口(了解下實現(xiàn)方式)

  • 后臺代碼
public class DateFormatter implements Formatter<Date> {

    private String datePattern;

    private SimpleDateFormat dateFormat;

    public DateFormatter(String datePattern) {
        this.datePattern = datePattern;
        dateFormat = new SimpleDateFormat(datePattern);

    }

    @Override
    public Date parse(String text, Locale locale) throws ParseException {
        Date result = null;
        try {
            result = dateFormat.parse(text);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return result;
    }

    @Override
    public String print(Date object, Locale locale) {
        return dateFormat.format(object);
    }
}
  • xml配置
<!--裝配自定義格式轉(zhuǎn)化器-->
<mvc:annotation-driven conversion-service="conversionService"/>
<!--配置自定義格式化轉(zhuǎn)換器bean-->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="formatters">
        <bean class="utils.DateFormatter" c:datePattern="yyyy-MM-dd"/>
    </property>
</bean>

3.3 使用系統(tǒng)內(nèi)置的轉(zhuǎn)換器(推薦這種方式)

當(dāng)然Spring本身就提供了很多內(nèi)置的轉(zhuǎn)換器,不需要我們再寫多余的代碼。比如上面我們自定義的時間格式轉(zhuǎn)化器。Spring內(nèi)置的org.springframework.format.datetime包中就有對應(yīng)的DateFormatter實現(xiàn)類。

我們只需要定義xml就可以了,如下。

<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="formatters">
        <bean class="org.springframework.format.datetime.DateFormatter" c:pattern="yyyy-MM-dd"/>
    </property>
</bean>

3.4 自定義使用FormatterRegister注冊Formatter(只需要了解,無須掌握,太麻煩)

前面我們直接在xml里面注冊Formatter實現(xiàn)類,那么我們還可以直接在xml里面注冊Registrar,來替代直接注冊Formatter。

  • 后臺代碼

自定義出FormatterRegistrar類

public class CustomerFormatterRegistrar implements FormatterRegistrar {

    private DateFormatter dateFormatter;

    public CustomerFormatterRegistrar(DateFormatter dateFormatter) {
        this.dateFormatter = dateFormatter;
    }

    @Override
    public void registerFormatters(FormatterRegistry registry) {
        registry.addFormatter(dateFormatter);
    }
}

自定義出DateFormmater類

public class DateFormatter implements Formatter<Date> {

    private String datePattern;

    private SimpleDateFormat dateFormat;

    public DateFormatter(String datePattern) {
        this.datePattern = datePattern;
        dateFormat = new SimpleDateFormat(datePattern);

    }

    @Override
    public Date parse(String text, Locale locale) throws ParseException {
        Date result = null;
        try {
            result = dateFormat.parse(text);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return result;
    }

    @Override
    public String print(Date object, Locale locale) {
        return dateFormat.format(object);
    }
}
  • xml配置
<!--裝配自定義格式轉(zhuǎn)化器-->
<mvc:annotation-driven conversion-service="conversionService"/>
<!--在Spring上下文定義出自定義的時間轉(zhuǎn)化器組件-->
<bean id="dateFormatter" class="utils.DateFormatter" c:datePattern="yyyy-MM-dd"></bean>

<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="formatterRegistrars">
        <bean class="utils.CustomerFormatterRegistrar" c:dateFormatter-ref="dateFormatter"></bean>
    </property>
</bean>

3.5 使用注解的方式來進行格式化工作(AnnotationFormatterFactory<A extends Annotation>)

前面的例子無論是自定義實現(xiàn)數(shù)據(jù)格式工作還是使用系統(tǒng)內(nèi)置的類,都需要通過進行繁瑣的xml配置。現(xiàn)在我們直接使用注解_Annotation的方式進行實現(xiàn)格式化工作。

org.springframework.format.annotation 定義了兩個格式化的注解類型

  • 1.DateTimeFormat

@DateTimeFormat 注解可以對java.util.Date、java.util.Calendar等時間類型的屬性進行標(biāo)注。該類支持下面三種互斥屬性

==互斥屬性指的是只能擁有其一,不然同時具備。==

//自定義時間格式
private String pattern;

private String stylePattern;

private ISO iso;

stylePattern

/**
 * Set the two character to use to format date values. The first character used for
 * the date style, the second is for the time style. Supported characters are
 * <ul>
 * <li>'S' = Small</li>短日期/時間的樣式
 * <li>'M' = Medium</li>中日期/時間的樣式
 * <li>'L' = Long</li>長日期/時間的樣式
 * <li>'F' = Full</li>完整日期/時間的樣式
 * <li>'-' = Omitted</li>忽略日期/時間的樣式
 * <ul>
 * This method mimics the styles supported by Joda-Time.
 * @param stylePattern two characters from the set {"S", "M", "L", "F", "-"}
 * @since 3.2
 */
public void setStylePattern(String stylePattern) {
    this.stylePattern = stylePattern;
}

IOS幾種可選值

formats.put(ISO.DATE, "yyyy-MM-dd");
formats.put(ISO.TIME, "HH:mm:ss.SSSZ");
formats.put(ISO.DATE_TIME, "yyyy-MM-dd'T'HH:mm:ss.SSSZ");
  • 2.NumberFormat

NumberFormat可對類似數(shù)字類型的屬性進行標(biāo)注,它擁有兩個互斥的屬性。

String pattern()

Style style()

style可選枚舉值

enum Style {

    /**
     * The default format for the annotated type: typically 'number' but possibly
     * 'currency' for a money type (e.g. {@code javax.money.MonetaryAmount)}.
     * @since 4.2
     */
    DEFAULT,

    /**
     * The general-purpose number format for the current locale.
     */
    NUMBER,

    /**
     * The percent format for the current locale.
     */
    PERCENT,

    /**
     * The currency format for the current locale.
     */
    CURRENCY
}

代碼演示

  • 后臺代碼新建需要進行數(shù)據(jù)轉(zhuǎn)換及格式化的類。省略了get、set方法
public class User implements Serializable {
    private String loginname;

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday;

    /**
     * 薪水,以財務(wù)格式接收
     */
    @NumberFormat(style = NumberFormat.Style.NUMBER, pattern = "#,###")
    private double salary;

    /**
     * 業(yè)績完成比例
     */
    @NumberFormat(style = NumberFormat.Style.PERCENT)
    private double performance;

    /**
     * 薪水的貨幣類型展示
     */
    @NumberFormat(style = NumberFormat.Style.CURRENCY)
    private double salaryDisplay;
}
  • xml配置
<mvc:annotation-driven/>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容