天天看點

SpringFramework核心技術三:Spring時間處理和類型轉換

使用

在(1)中,我們學習了蠻多的基本概念,在(2)中咱們看一下如何使用的問題。

一、以程式設計方式使用ConversionService

要以程式設計方式使用ConversionService執行個體,隻需為其他bean注入一個引用即可:

@Service
public class MyService {

    @Autowired
    public MyService(ConversionService conversionService) {
        this.conversionService = conversionService;
    }

    public void doIt() {
        this.conversionService.convert(...)
    }
}
           

對于大多數用例,可以使用convert指定targetType的方法,但它不适用于更複雜的類型,如參數化元素的集合。如果你想轉換List的Integer到List的String程式,例如,你需要提供的源和目标類型的正式定義。

幸運的是,

TypeDescriptor

提供了各種選項來簡單明了:

DefaultConversionService cs = new DefaultConversionService();

List<Integer> input = ....
cs.convert(input,
    TypeDescriptor.forObject(input), // List<Integer> type descriptor
    TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)));           

請注意,DefaultConversionService自動注冊轉換器适用于大多數環境。這包括收集器,标轉換器,也基本Object到String轉換器。相同的轉換器可以ConverterRegistry使用該類 上的靜态 addDefaultConverters方法進行注冊DefaultConversionService。

值類型轉換器将被重新用于數組和集合,是以沒有必要建立一個特定的轉換器從轉換Collection的S到 Collection的T,假設标準收集處理是适當的。

二、Spring字段格式

正如前一節所讨論的,core.convert是一種通用型轉換系統。它提供統一的ConversionService API以及用于實作從一種類型到另一種類型的轉換邏輯的強類型轉換器SPI。Spring容器使用這個系統綁定bean屬性值。另外,Spring表達式語言(SpEL)和DataBinder都使用這個系統來綁定字段值。例如,當使用SpEL需要強迫一個Short到一個Long完成的expression.setValue(Object bean, Object value)嘗試,core.convert系統執行強制。

現在考慮典型用戶端環境(如Web或桌面應用程式)的類型轉換要求。在這樣的環境中,您通常從String轉換 為支援用戶端回發過程,并傳回String以支援視圖呈現過程。另外,您經常需要本地化字元串值。更一般的core.convert Converter SPI沒有直接解決這種格式化要求。為了直接解決它們,Spring 3引入了一個友善的Formatter SPI,為用戶端環境提供了PropertyEditor的一個簡單而強大的替代方案。

通常,在需要實作通用類型轉換邏輯時使用Converter SPI; 例如,用于在java.util.Date和java.lang.Long之間進行轉換。在用戶端環境(如Web應用程式)中工作時需要使用Formatter SPI,并且需要解析和列印本地化的字段值。ConversionService為兩個SPI提供統一的類型轉換API。

1.格式化器SPI

格式化器SPI實作字段格式化邏輯很簡單并且強類型化:

package org.springframework.format;

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

Formatter從列印機和解析器建構塊接口擴充而來:

public interface Printer<T> {
    String print(T fieldValue, Locale locale);
}           
import java.text.ParseException;

public interface Parser<T> {
    T parse(String clientValue, Locale locale) throws ParseException;
}           

要建立自己的格式化程式,隻需實作上面的格式化接口即可。例如,将T指定為要格式化的對象的類型 java.util.Date。實施print()操作以列印T的執行個體以在用戶端區域設定中顯示。實作parse()操作以從客戶機語言環境傳回的格式化表示中解析T的執行個體。如果解析嘗試失敗,則格式化程式應該抛出ParseException或IllegalArgumentException。注意確定您的Formatter實作是線程安全的。

format為友善起見,在子包中提供了幾個格式化器實作。該number包提供了一個NumberFormatter,CurrencyFormatter并 使用a PercentFormatter來格式化java.lang.Number對象java.text.NumberFormat。該datetime包提供了一個用a DateFormatter來格式化java.util.Date對象java.text.DateFormat。該datetime.joda軟體包提供基于Joda-Time庫的綜合日期時間格式化支援。

考慮DateFormatter作為一個示例Formatter實作:

package org.springframework.format.datetime;

public final class DateFormatter implements Formatter<Date> {

    private String pattern;

    public DateFormatter(String pattern) {
        this.pattern = pattern;
    }

    public String print(Date date, Locale locale) {
        if (date == null) {
            return "";
        }
        return getDateFormat(locale).format(date);
    }

    public Date parse(String formatted, Locale locale) throws ParseException {
        if (formatted.length() == 0) {
            return null;
        }
        return getDateFormat(locale).parse(formatted);
    }

    protected DateFormat getDateFormat(Locale locale) {
        DateFormat dateFormat = new SimpleDateFormat(this.pattern, locale);
        dateFormat.setLenient(false);
        return dateFormat;
    }

}           

2.注釋驅動的格式

您将會看到,字段格式可以通過字段類型或注釋進行配置。要将注釋綁定到格式化程式,請實作AnnotationFormatterFactory:

package org.springframework.format;

public interface AnnotationFormatterFactory<A extends Annotation> {

    Set<Class<?>> getFieldTypes();

    Printer<?> getPrinter(A annotation, Class<?> fieldType);

    Parser<?> getParser(A annotation, Class<?> fieldType);

}           

例如,參數化A是您希望将格式邏輯與之關聯的字段批注類型org.springframework.format.annotation.DateTimeFormat。已經 getFieldTypes()傳回類型字段中的注釋,可以使用上。已經 getPrinter()傳回列印機列印的注釋字段的值。已經 getParser()傳回解析器解析一個clientValue一個注釋字段。

下面的示例AnnotationFormatterFactory實作将@NumberFormat注釋綁定到格式化程式。此注釋允許指定數字樣式或模式:

public final class NumberFormatAnnotationFormatterFactory
        implements AnnotationFormatterFactory<NumberFormat> {

    public Set<Class<?>> getFieldTypes() {
        return new HashSet<Class<?>>(asList(new Class<?>[] {
            Short.class, Integer.class, Long.class, Float.class,
            Double.class, BigDecimal.class, BigInteger.class }));
    }

    public Printer<Number> getPrinter(NumberFormat annotation, Class<?> fieldType) {
        return configureFormatterFrom(annotation, fieldType);
    }

    public Parser<Number> getParser(NumberFormat annotation, Class<?> fieldType) {
        return configureFormatterFrom(annotation, fieldType);
    }

    private Formatter<Number> configureFormatterFrom(NumberFormat annotation,
            Class<?> fieldType) {
        if (!annotation.pattern().isEmpty()) {
            return new NumberFormatter(annotation.pattern());
        } else {
            Style style = annotation.style();
            if (style == Style.PERCENT) {
                return new PercentFormatter();
            } else if (style == Style.CURRENCY) {
                return new CurrencyFormatter();
            } else {
                return new NumberFormatter();
            }
        }
    }
}           

要觸發格式化,隻需使用@NumberFormat注釋字段即可:

public class MyModel {

    @NumberFormat(style=Style.CURRENCY)
    private BigDecimal decimal;

}           
  • 格式注釋API

    org.springframework.format.annotation

    包中存在可移植的格式注釋API 。使用

    @NumberFormat

    格式化

    java.lang.Number

    字段。使用

    @DateTimeFormat

    java.util.Date,java.util.Calendar,java.util.Long

    或Joda-Time字段。

以下示例使用

@DateTimeFormat

java.util.Date

格式化為ISO日期(yyyy-MM-dd):

public class MyModel {

    @DateTimeFormat(iso=ISO.DATE)
    private Date date;

}           

3.FormatterRegistry SPI

FormatterRegistry

是用于注冊格式化程式和轉換程式的SPI。

FormattingConversionService

是适用于大多數環境的

FormatterRegistry的

實作。這個實作可以通過程式設計或聲明方式配置為Spring bean

FormattingConversionServiceFactoryBean

。因為這個實作也實作了

ConversionService

,是以它可以直接配置用于Spring的DataBinder和Spring表達式語言(SpEL)。

檢視下面的FormatterRegistry SPI:

package org.springframework.format;

public interface FormatterRegistry extends ConverterRegistry {

    void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser);

    void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter);

    void addFormatterForFieldType(Formatter<?> formatter);

    void addFormatterForAnnotation(AnnotationFormatterFactory<?, ?> factory);

}           

如上所示,格式化程式可以通過fieldType或注釋進行注冊。

FormatterRegistry SPI允許您集中配置格式化規則,而不是在控制器中複制這種配置。例如,您可能希望強制所有日期字段以特定方式格式化,或者使用特定批注的字段以特定方式格式化。通過共享的FormatterRegistry,您可以定義這些規則一次,并在需要格式化時應用這些規則。

4.FormatterRegistrar SPI

FormatterRegistrar是一個用于通過FormatterRegistry注冊格式化器和轉換器的SPI:

package org.springframework.format;

public interface FormatterRegistrar {

    void registerFormatters(FormatterRegistry registry);

}           

FormatterRegistrar在為給定格式類别(例如日期格式)注冊多個相關轉換器和格式化程式時非常有用。在聲明性注冊不足的情況下,它也很有用。例如,格式化程式需要在與其自己的不同的特定字段類型下或在注冊列印機/解析器對時進行索引。下一節提供了有關轉換器和格式化程式注冊的更多資訊。

三、配置全球日期和時間格式

預設情況下,不使用注釋的日期和時間字段@DateTimeFormat使用DateFormat.SHORT樣式從字元串轉換。如果你願意,你可以通過定義你自己的全局格式來改變它。

您需要確定Spring不會注冊預設格式化程式,而應該手動注冊所有格式化程式。根據您是否使用Joda-Time庫,使用

org.springframework.format.datetime.joda.JodaTimeFormatterRegistraror

org.springframework.format.datetime.DateFormatterRegistrarclass

例如,以下Java配置将注冊全局“yyyyMMdd”格式。這個例子不依賴于Joda-Time庫:

@Configuration
public class AppConfig {

    @Bean
    public FormattingConversionService conversionService() {

        // Use the DefaultFormattingConversionService but do not register defaults
        DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false);

        // Ensure @NumberFormat is still supported
        conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());

        // Register date conversion with a specific global format
        DateFormatterRegistrar registrar = new DateFormatterRegistrar();
        registrar.setFormatter(new DateFormatter("yyyyMMdd"));
        registrar.registerFormatters(conversionService);

        return conversionService;
    }
}           

如果你更喜歡基于XML的配置,你可以使用a

FormattingConversionServiceFactoryBean

。這是同一個例子,這次使用喬達時間:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd>

    <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="registerDefaultFormatters" value="false" />
        <property name="formatters">
            <set>
                <bean class="org.springframework.format.number.NumberFormatAnnotationFormatterFactory" />
            </set>
        </property>
        <property name="formatterRegistrars">
            <set>
                <bean class="org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar">
                    <property name="dateFormatter">
                        <bean class="org.springframework.format.datetime.joda.DateTimeFormatterFactoryBean">
                            <property name="pattern" value="yyyyMMdd"/>
                        </bean>
                    </property>
                </bean>
            </set>
        </property>
    </bean>
</beans>           
喬達時提供單獨的不同類型來表示date,time和date-time 的值。的dateFormatter,timeFormatter和dateTimeFormatter的性質 JodaTimeFormatterRegistrar,應使用來配置不同的格式為每種類型。在DateTimeFormatterFactoryBean提供了一個友善的方法來建立格式化。

如果您使用Spring MVC,請記住明确配置使用的轉換服務。對于基于Java的@Configuration這意味着擴充 WebMvcConfigurationSupport類和重寫mvcConversionService()方法。對于XML,您應該使用元素的’conversion-service’屬性 mvc:annotation-driven。有關詳細資訊,請參閱轉換和格式。