此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10spring-doc.cadn.net.cn

弹簧字段格式

如上一节所述,core.convert是一个 通用型转换系统。它提供了一个统一的ConversionServiceAPI 作为 以及强类型的ConverterSPI 用于实现从一种类型实现转换逻辑 给另一个。Spring 容器使用此系统来绑定 bean 属性值。在 此外,Spring 表达式语言 (SpEL) 和DataBinder使用此系统 绑定字段值。例如,当 SpEL 需要强制Short设置为Long自 完成一个expression.setValue(Object bean, Object value)尝试,则core.convert系统执行强制。spring-doc.cadn.net.cn

现在考虑典型客户端环境的类型转换要求,例如 Web 或桌面应用程序。在此类环境中,您通常从String支持客户端回发过程,以及返回String以支持 视图渲染过程。此外,您经常需要进行本地化String值。更多 常规core.convert ConverterSPI 不满足此类格式要求 径直。为了直接解决它们,Spring 提供了一个方便的FormatterSPI 该 提供了一个简单而强大的替代方案PropertyEditor客户端的实现 环境。spring-doc.cadn.net.cn

通常,您可以使用ConverterSPI 当您需要实现通用类型时 转换逻辑 — 例如,用于在java.util.DateLong. 您可以使用Formatter在客户端环境(例如 Web application),需要解析和打印本地化字段值。这ConversionService为两个 SPI 提供统一的类型转换 API。spring-doc.cadn.net.cn

FormatterSPI

Formatter实现字段格式化逻辑的 SPI 是简单且强类型的。这 以下列表显示了Formatter接口定义:spring-doc.cadn.net.cn

package org.springframework.format;

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

FormatterPrinterParser构建块接口。这 以下列表显示了这两个接口的定义:spring-doc.cadn.net.cn

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

创建您自己的Formatter,实现Formatter前面显示的界面。 参数化T设置为要格式化的对象类型 - 例如,java.util.Date.实现print()作以打印T为 显示在客户端区域设置中。实现parse()作来解析T从客户端区域设置返回的格式化表示形式。你Formatter应该抛出一个ParseExceptionIllegalArgumentException如果解析尝试失败。拿 注意确保您的Formatter实现是线程安全的。spring-doc.cadn.net.cn

format子包提供了几个Formatter为了方便起见。 这numberpackage 提供NumberStyleFormatter,CurrencyStyleFormatterPercentStyleFormatter格式化Number使用java.text.NumberFormat. 这datetimepackage 提供了一个DateFormatter格式化java.util.Date对象与 一个java.text.DateFormat,以及DurationFormatter格式化Duration对象 在@DurationFormat.Style枚举(请参阅格式注释 API)。spring-doc.cadn.net.cn

以下内容DateFormatter就是一个例子Formatter实现:spring-doc.cadn.net.cn

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;
	}
}
class DateFormatter(private val pattern: String) : Formatter<Date> {

	override fun print(date: Date, locale: Locale)
			= getDateFormat(locale).format(date)

	@Throws(ParseException::class)
	override fun parse(formatted: String, locale: Locale)
			= getDateFormat(locale).parse(formatted)

	protected fun getDateFormat(locale: Locale): DateFormat {
		val dateFormat = SimpleDateFormat(this.pattern, locale)
		dateFormat.isLenient = false
		return dateFormat
	}
}

Spring 团队欢迎社区驱动Formatter贡献。请参阅 GitHub 问题进行贡献。spring-doc.cadn.net.cn

注释驱动的格式

字段格式可以按字段类型或注释进行配置。绑定 对Formatter实现AnnotationFormatterFactory.以下内容 列表中显示了AnnotationFormatterFactory接口:spring-doc.cadn.net.cn

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);
}

要创建实现,请执行以下作:spring-doc.cadn.net.cn

  1. 参数化A成为领域annotationType您希望与之关联 格式化逻辑 — 例如org.springframework.format.annotation.DateTimeFormat.spring-doc.cadn.net.cn

  2. getFieldTypes()返回可以使用注释的字段类型。spring-doc.cadn.net.cn

  3. getPrinter()返回一个Printer以打印带注释的字段的值。spring-doc.cadn.net.cn

  4. getParser()返回一个Parser解析clientValue对于带注释的字段。spring-doc.cadn.net.cn

以下示例AnnotationFormatterFactory实现绑定了@NumberFormat注释到格式化程序中,以指定数字样式或模式:spring-doc.cadn.net.cn

public final class NumberFormatAnnotationFormatterFactory
		implements AnnotationFormatterFactory<NumberFormat> {

	private static final Set<Class<?>> FIELD_TYPES = Set.of(Short.class,
			Integer.class, Long.class, Float.class, Double.class,
			BigDecimal.class, BigInteger.class);

	public Set<Class<?>> getFieldTypes() {
		return FIELD_TYPES;
	}

	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
		return switch(annotation.style()) {
			case Style.PERCENT -> new PercentStyleFormatter();
			case Style.CURRENCY -> new CurrencyStyleFormatter();
			default -> new NumberStyleFormatter();
		};
	}
}
class NumberFormatAnnotationFormatterFactory : AnnotationFormatterFactory<NumberFormat> {

	override fun getFieldTypes(): Set<Class<*>> {
		return setOf(Short::class.java, Int::class.java, Long::class.java, Float::class.java, Double::class.java, BigDecimal::class.java, BigInteger::class.java)
	}

	override fun getPrinter(annotation: NumberFormat, fieldType: Class<*>): Printer<Number> {
		return configureFormatterFrom(annotation, fieldType)
	}

	override fun getParser(annotation: NumberFormat, fieldType: Class<*>): Parser<Number> {
		return configureFormatterFrom(annotation, fieldType)
	}

	private fun configureFormatterFrom(annotation: NumberFormat, fieldType: Class<*>): Formatter<Number> {
		return if (annotation.pattern.isNotEmpty()) {
			NumberStyleFormatter(annotation.pattern)
		} else {
			val style = annotation.style
			when {
				style === NumberFormat.Style.PERCENT -> PercentStyleFormatter()
				style === NumberFormat.Style.CURRENCY -> CurrencyStyleFormatter()
				else -> NumberStyleFormatter()
			}
		}
	}
}

要触发格式设置,您可以使用@NumberFormat,如下所示 示例显示:spring-doc.cadn.net.cn

public class MyModel {

	@NumberFormat(style=Style.CURRENCY)
	private BigDecimal decimal;
}
class MyModel(
	@field:NumberFormat(style = Style.CURRENCY) private val decimal: BigDecimal
)

格式注释 API

可移植格式注释 API 存在于org.springframework.format.annotation包。您可以使用@NumberFormat格式化Number字段,例如DoubleLong,@DurationFormat格式化DurationISO-8601 和简化样式中的字段, 和@DateTimeFormat设置字段的格式,例如java.util.Date,java.util.Calendar, 和Long(对于毫秒时间戳)以及 JSR-310java.time类型。spring-doc.cadn.net.cn

以下示例使用@DateTimeFormat格式化java.util.Date作为 ISO 日期 (yyyy-MM-dd):spring-doc.cadn.net.cn

public class MyModel {

	@DateTimeFormat(iso=ISO.DATE)
	private Date date;
}
class MyModel(
	@DateTimeFormat(iso=ISO.DATE) private val date: Date
)

有关更多详细信息,请参阅 javadoc@DateTimeFormat,@DurationFormat@NumberFormat.spring-doc.cadn.net.cn

基于样式的格式和解析依赖于区域设置敏感模式,这些模式可能会发生变化 取决于 Java 运行时。具体而言,依赖于日期、时间或 数字解析和格式化在运行时可能会遇到不兼容的行为变化 在 JDK 20 或更高版本上。spring-doc.cadn.net.cn

使用 ISO 标准化格式或您控制的具体模式允许 可靠的独立于系统和语言环境的日期、时间、 和数字值。spring-doc.cadn.net.cn

@DateTimeFormat,使用回退模式也有助于解决 兼容性问题。spring-doc.cadn.net.cn

有关更多详细信息,请参阅 Spring Framework wiki 中的 JDK 20 及更高版本的日期和时间格式化页面。spring-doc.cadn.net.cn

FormatterRegistrySPI

FormatterRegistry是用于注册格式化器和转换器的 SPI。FormattingConversionServiceFormatterRegistry适合人群 大多数环境。您可以通过编程或声明方式配置此变体 作为 Spring bean,例如,通过使用FormattingConversionServiceFactoryBean.因为这个 实现也实现ConversionService,可以直接配置 与 Spring 的DataBinder和 Spring 表达式语言 (SpEL)。spring-doc.cadn.net.cn

以下列表显示了FormatterRegistrySPI:spring-doc.cadn.net.cn

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);
}

如前面的列表所示,您可以按字段类型或注释注册格式化程序。spring-doc.cadn.net.cn

FormatterRegistrySPI 允许您集中配置格式规则,而不是 在控制器之间复制此类配置。例如,您可能想要 强制所有日期字段都以特定方式格式化,或者将字段设置为特定的 注释以某种方式格式化。使用共享的FormatterRegistry,则定义 这些规则一次,并在需要格式化时应用它们。spring-doc.cadn.net.cn

FormatterRegistrarSPI

FormatterRegistrar是一个 SPI,用于通过 FormatterRegistry 中。以下列表显示了其接口定义:spring-doc.cadn.net.cn

package org.springframework.format;

public interface FormatterRegistrar {

	void registerFormatters(FormatterRegistry registry);
}

一个FormatterRegistrar在注册多个相关转换器时很有用,并且 给定格式类别的格式化程序,例如日期格式。也可以是 在声明性注册不足的情况下很有用,例如,当格式化程序 需要在与其自身不同的特定字段类型下建立索引<T>或者当 注册一个Printer/Parser双。下一节将提供有关 转换器和格式化程序注册。spring-doc.cadn.net.cn

在 Spring MVC 中配置格式

请参阅 Spring MVC 章节中的转换和格式化spring-doc.cadn.net.cn