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

Spring Field 格式设置

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

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

通常,您可以使用ConverterSPI 当您需要实现通用型 conversion logic — 例如,用于在java.util.Date以及Long. 您可以使用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;
}

To create your own Formatter, implement the Formatter interface shown earlier. Parameterize T to be the type of object you wish to format — for example, java.util.Date. Implement the print() operation to print an instance of T for display in the client locale. Implement the parse() operation to parse an instance of T from the formatted representation returned from the client locale. Your Formatter should throw a ParseException or an IllegalArgumentException if a parse attempt fails. Take care to ensure that your Formatter implementation is thread-safe.spring-doc.cadn.net.cn

The format subpackages provide several Formatter implementations as a convenience. The number package provides NumberStyleFormatter, CurrencyStyleFormatter, and PercentStyleFormatter to format Number objects that use a java.text.NumberFormat. The datetime package provides a DateFormatter to format java.util.Date objects with a java.text.DateFormat.spring-doc.cadn.net.cn

The following DateFormatter is an example Formatter implementation: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
	}
}

The Spring team welcomes community-driven Formatter contributions. See GitHub Issues to contribute.spring-doc.cadn.net.cn

Annotation-driven Formatting

Field formatting can be configured by field type or annotation. To bind an annotation to a Formatter, implement AnnotationFormatterFactory. The following listing shows the definition of the AnnotationFormatterFactory interface: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);
}

To create an implementation:spring-doc.cadn.net.cn

  1. Parameterize A to be the field annotationType with which you wish to associate formatting logic — for example org.springframework.format.annotation.DateTimeFormat.spring-doc.cadn.net.cn

  2. Have getFieldTypes() return the types of fields on which the annotation can be used.spring-doc.cadn.net.cn

  3. Have getPrinter() return a Printer to print the value of an annotated field.spring-doc.cadn.net.cn

  4. Have getParser() return a Parser to parse a clientValue for an annotated field.spring-doc.cadn.net.cn

The following example AnnotationFormatterFactory implementation binds the @NumberFormat annotation to a formatter to let a number style or pattern be specified: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()
			}
		}
	}
}

To trigger formatting, you can annotate fields with @NumberFormat, as the following example shows: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
)

Format Annotation API

A portable format annotation API exists in the org.springframework.format.annotation package. You can use @NumberFormat to format Number fields such as Double and Long, and @DateTimeFormat to format fields such as java.util.Date, java.util.Calendar, and Long (for millisecond timestamps) as well as JSR-310 java.time types.spring-doc.cadn.net.cn

The following example uses @DateTimeFormat to format a java.util.Date as an ISO date (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
)

For further details, see the javadoc for @DateTimeFormat and @NumberFormat.spring-doc.cadn.net.cn

Style-based formatting and parsing rely on locale-sensitive patterns which may change depending on the Java runtime. Specifically, applications that rely on date, time, or number parsing and formatting may encounter incompatible changes in behavior when running on JDK 20 or higher.spring-doc.cadn.net.cn

Using an ISO standardized format or a concrete pattern that you control allows for reliable system-independent and locale-independent parsing and formatting of date, time, and number values.spring-doc.cadn.net.cn

For @DateTimeFormat, the use of fallback patterns can also help to address compatibility issues.spring-doc.cadn.net.cn

For further details, see the Date and Time Formatting with JDK 20 and higher page in the Spring Framework wiki.spring-doc.cadn.net.cn

The FormatterRegistry SPI

The FormatterRegistry is an SPI for registering formatters and converters. FormattingConversionService is an implementation of FormatterRegistry suitable for most environments. You can programmatically or declaratively configure this variant as a Spring bean, e.g. by using FormattingConversionServiceFactoryBean. Because this implementation also implements ConversionService, you can directly configure it for use with Spring’s DataBinder and the Spring Expression Language (SpEL).spring-doc.cadn.net.cn

The following listing shows the FormatterRegistry SPI: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);
}

As shown in the preceding listing, you can register formatters by field type or by annotation.spring-doc.cadn.net.cn

The FormatterRegistry SPI lets you configure formatting rules centrally, instead of duplicating such configuration across your controllers. For example, you might want to enforce that all date fields are formatted a certain way or that fields with a specific annotation are formatted in a certain way. With a shared FormatterRegistry, you define these rules once, and they are applied whenever formatting is needed.spring-doc.cadn.net.cn

The FormatterRegistrar SPI

FormatterRegistrar is an SPI for registering formatters and converters through the FormatterRegistry. The following listing shows its interface definition:spring-doc.cadn.net.cn

package org.springframework.format;

public interface FormatterRegistrar {

	void registerFormatters(FormatterRegistry registry);
}

A FormatterRegistrar is useful when registering multiple related converters and formatters for a given formatting category, such as date formatting. It can also be useful where declarative registration is insufficient — for example, when a formatter needs to be indexed under a specific field type different from its own <T> or when registering a Printer/Parser pair. The next section provides more information on converter and formatter registration.spring-doc.cadn.net.cn

Configuring Formatting in Spring MVC

See Conversion and Formatting in the Spring MVC chapter.spring-doc.cadn.net.cn