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之间进行转换Long。Formatter当您在客户端环境(例如 Web 应用程序)中工作并且需要解析和打印本地化的字段值时,您可以使用SPI。ConversionService 为两个 SPI 提供了统一的类型转换 API 。
3.5.1. FormatterSPI
实现字段格式化逻辑的FormatterSPI 简单且强类型。以下清单显示了Formatter接口定义:
package org.springframework.format;
public interface Formatter<T> extends Printer<T>, Parser<T> {
}Formatter从Printer和Parser构建块接口进行扩展。以下清单显示了这两个接口的定义:
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 包提供 NumberStyleFormatter、CurrencyStyleFormatter 和PercentStyleFormatter来格式化使用 java.text.NumberFormat 的 Number 对象。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);
}创建一个实现:
将
annotationType参数化为您希望与格式化逻辑相关联的字段 - 例如org.springframework.format.annotation.DateTimeFormat.getFieldTypes()已返回可以使用注解的字段类型。getPrinter()返回Printer以打印带注解的字段的值。已
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字段,例如Double和 Long,@DateTimeFormat格式化java.util.Date, java.util.Calendar,Long (用于毫秒时间戳)以及 JSR-310 java.time。
以下示例用于@DateTimeFormat将java.util.Date 格式化为 ISO 日期 (yyyy-MM-dd):
public class MyModel {
@DateTimeFormat(iso=ISO.DATE)
private Date date;
}3.5.3. FormatterRegistrySPI
FormatterRegistry是一个用于注册格式化程序和转换器的 SPI。 FormattingConversionService是FormatterRegistry适用于大多数环境的实现。您可以以编程方式或声明方式将此变体配置为 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 章节中的转换和格式化。
最后更新于
这有帮助吗?