Spring Field Formatting
The Formatter SPI
Formatter接口
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表示我们需要通过string解析成的类比如java.util.Date
类型。同时需要注意Formatter和Converter一样都需要是线程安全的,如下是spring提供的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;
}
}
基于注解的Formatter
顶级接口
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提供的实现类
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();
}
}
}
}
以及相应的注解
public class MyModel {
@NumberFormat(style=Style.CURRENCY)
private BigDecimal decimal;
}
The FormatterRegistry SPI
有实现类那么当然会有注册的类FormatterRegistry
就是用于注册Formatter
的接口。
下面提供一个比较简单的Spring boot实现Formatter的方法,Spring自身会提供Formatter,具体类型可以自己注入然后输出类型查看。
Configuration
@Configuration
public class FormatterConfigTest {
@Bean
public FormatterRegistry formatterRegistry(FormatterRegistry formatter){
formatter.addFormatterForFieldAnnotation(new AnnotationFormatterFactory<TestMyFomatter>() {
@Override
public Set<Class<?>> getFieldTypes() {
return new HashSet<>(Arrays.asList(Person.class));
}
@Override
public Printer<?> getPrinter(TestMyFomatter testMyFomatter, Class<?> aClass) {
return new MyFormatter();
}
@Override
public Parser<?> getParser(TestMyFomatter testMyFomatter, Class<?> aClass) {
return new MyFormatter();
}
});
return formatter;
}
}
注解类
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
public @interface TestMyFomatter {
}
Person Bean
import lombok.Data;
import org.springframework.stereotype.Component;
@Data
public class Person {
private String name;
private int age;
}
Formatter
public class MyFormatter implements Formatter<Person> {
@Override
public Person parse(String s, Locale locale) throws ParseException {
String[] temp = s.split("H");
Person person = new Person();
person.setName(temp[0]);
person.setAge(Integer.parseInt(temp[1]));
return person;
}
@Override
public String print(Person person, Locale locale) {
return person.getAge()+"H"+person.getName();
}
}
Controller
@RestController
public class TestValidatorController {
@GetMapping("/validate")
public String validate(@TestMyFomatter @RequestParam("person") Person person){
System.out.println(person);
return person.getAge()+" "+person.getName();
}
}
启动项目然后输入 hostname:端口/validate?person=nameH12323可以看到页面输出为12323 name证明Formatter生效,注意该方法中没有使用Local,这个是java提供的用来实现国际化的类。我们还可以在Configuration的getParser()方法中加入输出或者log,发现getParser()只执行了一次,所以在整个容器的转换过程中,Parse的MyFormatter对象唯一,所以需要注意线程安全问题。
The FormatterRegistrar SPI
package org.springframework.format;
public interface FormatterRegistrar {
void registerFormatters(FormatterRegistry registry);
}
很容易看出这个接口是实现Formatter的注入的
Configuring Formatting in Spring MVC
Spring mvc也提供了相应接口供我们实现formatter注入 查看