3.5. spring字段格式

正如上一节所讨论的,core.convert是一个通用的类型转换系统。它提供了一个统一的ConversionServiceAPI 以及一个强类型的ConverterSPI,用于实现从一种类型到另一种类型的转换逻辑。Spring 容器使用此系统绑定 bean 属性值。此外,Spring 表达式语言(SpEL)都使用这个系统来绑定字段值。例如,当 SpEL 需要将 Short强制转换为Long以完成一次expression.setValue(Object bean, Object value)尝试时,core.convert 系统会执行强制。

现在考虑典型客户端环境的类型转换要求,例如 Web 或桌面应用程序。在这样的环境中,您通常转换String 以支持客户端回发过程,以及转换回String以支持视图呈现过程。此外,您经常需要本地化String值。更通用的core.convert ConverterSPI 不直接解决此类格式要求。为了直接解决这些问题,Spring 3 引入了一个方便的FormatterSPI,它为客户端环境的实现提供了一个简单而健壮的替代方案。PropertyEditor

Converter通常,当您需要实现通用类型转换逻辑时,您可以使用SPI——例如,在 ajava.util.Date和 a之间进行转换LongFormatter当您在客户端环境(例如 Web 应用程序)中工作并且需要解析和打印本地化的字段值时,您可以使用SPI。ConversionService 为两个 SPI 提供了统一的类型转换 API 。

3.5.1. FormatterSPI

实现字段格式化逻辑的FormatterSPI 简单且强类型。以下清单显示了Formatter接口定义:

package org.springframework.format;

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

FormatterPrinterParser构建块接口进行扩展。以下清单显示了这两个接口的定义:

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 的实例。如果解析尝试失败,您的 Formatter应该抛出 ParseException IllegalArgumentException。请注意确保您的 Formatter 实现是线程安全的。

为了方便起见,格式子包提供了几种 Formatter 实现。 number 包提供 NumberStyleFormatterCurrencyStyleFormatterPercentStyleFormatter来格式化使用 java.text.NumberFormatNumber 对象。datetime包提供了一个 DateFormatter 来使用java.text.DateFormat来格式化java.util.Date对象。

下面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;
    }
}

Spring 团队欢迎社区驱动的Formatter贡献。请参阅 GitHub 问题以做出贡献。

3.5.2. 注解驱动的格式化

字段格式可以通过字段类型或注解进行配置。要将注解绑定到 Formatter,请实现AnnotationFormatterFactory. 以下清单显示了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);
}

创建一个实现:

  1. annotationType参数化为您希望与格式化逻辑相关联的字段 - 例如org.springframework.format.annotation.DateTimeFormat.

  2. getFieldTypes()已返回可以使用注解的字段类型。

  3. getPrinter()返回Printer以打印带注解的字段的值。

  4. getParser()返回 Parser以解析带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 NumberStyleFormatter(annotation.pattern());
        } else {
            Style style = annotation.style();
            if (style == Style.PERCENT) {
                return new PercentStyleFormatter();
            } else if (style == Style.CURRENCY) {
                return new CurrencyStyleFormatter();
            } else {
                return new NumberStyleFormatter();
            }
        }
    }
}

要触发格式化,您可以使用@NumberFormat 注解字段,如以下示例所示:

public class MyModel {

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

格式注解 API

I org.springframework.format.annotation包中存在可移植格式注解 AP 。您可以使用@NumberFormat格式化Number字段,例如DoubleLong@DateTimeFormat格式化java.util.Date, java.util.CalendarLong (用于毫秒时间戳)以及 JSR-310 java.time

以下示例用于@DateTimeFormatjava.util.Date 格式化为 ISO 日期 (yyyy-MM-dd):

public class MyModel {

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

3.5.3. FormatterRegistrySPI

FormatterRegistry是一个用于注册格式化程序和转换器的 SPI。 FormattingConversionServiceFormatterRegistry适用于大多数环境的实现。您可以以编程方式或声明方式将此变体配置为 Spring bean,例如使用FormattingConversionServiceFactoryBean. 由于此实现还实现了ConversionService ,因此您可以直接将其配置为与 SpringDataBinder和 Spring 表达式语言 (SpEL) 一起使用。

以下清单显示了FormatterRegistrySPI:

package org.springframework.format;

public interface FormatterRegistry extends ConverterRegistry {

    void addPrinter(Printer<?> printer);

    void addParser(Parser<?> parser);

    void addFormatter(Formatter<?> formatter);

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

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

    void addFormatterForFieldAnnotation(AnnotationFormatterFactory<? extends Annotation> annotationFormatterFactory);
}

如前面的清单所示,您可以按字段类型或注解注册格式化程序。

SPI 让您可以集中配置格式规则,而不是在FormatterRegistry控制器之间复制此类配置。例如,您可能希望强制所有日期字段都以某种方式格式化,或者具有特定注解的字段以某种方式格式化。使用共享的FormatterRegistry,您只需定义一次这些规则,并在需要格式化时应用它们。

3.5.4. FormatterRegistrarSPI

FormatterRegistrar是一个 SPI,用于通过 FormatterRegistry 注册格式化程序和转换器。下面的清单显示了它的接口定义:

package org.springframework.format;

public interface FormatterRegistrar {

    void registerFormatters(FormatterRegistry registry);
}

在为给定的格式类别(例如日期格式)注册多个相关转换器和格式器时,FormatterRegistrar很有用。在声明式注册不足的情况下,它也很有用——例如,当格式化程序需要在与其自身<T>不同的特定字段类型下进行索引时,或者在注册Printer/Parser对时。下一节提供有关转换器和格式化程序注册的更多信息。

3.5.5. 在 Spring MVC 中配置格式化

请参阅Spring MVC 章节中的转换和格式化

最后更新于