Convert理解_第⼀弹:HttpMessageConverter,GenericCon。
。。
⼀、 HttpMessageConverter
简单说就是 HTTP request (请求)和response (响应)的转换器 ,当请求和响应时,根据 MediaType 顺序选择注册的合适的类别的HttpMessageConverter对数据进⾏处理。
HTTP 请求和响应是基于⽂本的,意味着浏览器和服务器通过交换原始⽂本进⾏通信。但是,使⽤ Spring,controller 类中的⽅法返回纯‘String’ 类型和域模型(或其他 Java 内建对象)。如何将对象序列化/反序列化为原始⽂本?这由HttpMessageConverter 处理。
Http请求和响应报⽂本质上都是⼀串字符串,当请求报⽂来到java世界,它会被封装成为⼀个ServletInputStream的输⼊流,供我们读取报⽂。响应报⽂则是通过⼀个ServletOutputStream的输出流,来输出响应报⽂。我们从流中,只能读取到原始的字符串报⽂,同样,我们往输出流中,也只能写原始的字符。⽽在java世界中,处理业务逻辑,都是以⼀个个有业务意义的对象为处理维度的,那么在报⽂到达SpringMVC和从SpringMVC出去,都存在⼀个字符串到java对象的阻抗问题。这⼀过程,不可能由开发者⼿⼯转换。我们知道,在Struts2中,采⽤了OGNL来应对这个问题,⽽在SpringMVC中,它是HttpMessageConverter机制。
public interface HttpMessageConverter<T> {
boolean canRead(Class<?> clazz, MediaType mediaType);
boolean canWrite(Class<?> clazz, MediaType mediaType);
List<MediaType> getSupportedMediaTypes();
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
Spring MVC的@ResponseBody 的作⽤是把返回值直接写到HTTP response body⾥。 使⽤AnnotationMethodHandlerAdapter的handleResponseBody⽅法, AnnotationMethodHandlerAdapter使⽤request header中”Accept”的值和messageConverter⽀持的MediaType进⾏匹配,然后会⽤”Accept”的第⼀个值写⼊ response的”Content-Type”。
AnnotationMethodHandlerAdapter将会初始化7个转换器,可以通过调⽤AnnotationMethodHandlerAdapter的getMessageConverts()⽅法来获取转换器的⼀个集合
⾃定义HttpMessageConverter可参考其他博⽂。
⼆、GenericConverter Converter
GenericConverter Converter 都是 数据转换类,系统初始化时注册到FormatterRegistry 类, 对请求的参数进⾏类别转换,⽐如 字符串转为⽇期,字符串转为list
FormatterRegistry是⼀个⽤于注册格式化器和转换器的SPI。 FormattingConversionService是FormatterRegistry适合⼤多数环境的实现。该FormatterRegistrySPI允许您配置集中格式规则,⽽不是在你的控制器重复这样的配置。例如,您可能希望强制所有⽇期字段以某种⽅式格式化,或者具有特
定注释的字段以某种⽅式格式化。
Converter主要是做Object与Object之间的类型转换,Formatter则是要完成任意Object与String之间的类型转换。前者适合于任何⼀层,⽽后者则主要⽤于web层
Converter只完成了数据类型的转换,却不负责输⼊输出数据的格式化⼯作,⽇期时间、货币等虽都以字符串形式存在,却有不同的格式。
Spring格式化框架要解决的问题是:从格式化的数据中获取真正的数据,绑定数据,将处理完成的数据输出为格式化的数据。Formatter接⼝就承担着这样的责任.
⾃定义Converter的场景其实蛮多的,⽐如最常见的StringToPhoneNumberConverter它俩的互转,就可以定义个转换器,⽀持中间空格的电话号码格式~
三、Formatter
1 . 代码查看
FormatterRegistry
从接⼝继承关系中可以看出,它既可以注册格式化器,⼜可议注册转换器
// @since 3.0
public interface FormatterRegistry extends ConverterRegistry {
void addFormatter(Formatter<?> formatter);
void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter);
// 单独指定Printer和parser也是被允许的
void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser);
// 注册处理注解的格式化器~~~~~ AnnotationFormatterFactory的实现类~~
void addFormatterForFieldAnnotation(AnnotationFormatterFactory<? extends Annotation> annotationFormatterFactory);
}
FormattingConversionService
// @since 3.0  它继承⾃GenericConversionService ,所以它能对Converter进⾏⼀系列的操作~~~
// 实现了接⼝FormatterRegistry,所以它也可以注册格式化器了
// 实现了EmbeddedValueResolverAware,所以它还能有⾮常强⼤的功能:处理占位
public class FormattingConversionService extends GenericConversionService implements FormatterRegistry, EmbeddedValueResolverAware {
@Nullable
private StringValueResolver embeddedValueResolver;
private final Map<AnnotationConverterKey, GenericConverter> cachedPrinters = new ConcurrentHashMap<>(64);
private final Map<AnnotationConverterKey, GenericConverter> cachedParsers = new ConcurrentHashMap<>(64);
// 最终也是交给addFormatterForFieldType去做的
// getFieldType:它会拿到泛型类型。并且⽀持DecoratingProxy~~~
@Override
public void addFormatter(Formatter<?> formatter) {
addFormatterForFieldType(getFieldType(formatter), formatter);
}
// 存储都是分开存储的读写分离
// PrinterConverter和ParserConverter都是⼀个GenericConverter  采⽤内部类实现的~~~  this代表⼀个ConversionService
// 注意:他们的ConvertiblePair必有⼀个类型是String.class
// Locale⼀般都可以这么获取:Locale()
// 最终parse出来的result有可能也会交给vert()  若类型能够匹配上的话
@Override
public void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter) {
addConverter(new PrinterConverter(fieldType, formatter, this));
addConverter(new ParserConverter(fieldType, formatter, this));
}
// 哪怕你是⼀个AnnotationFormatterFactory,最终也是被适配成了GenericConverter(ConditionalGenericConverter)
@Override
public void addFormatterForFieldAnnotation(AnnotationFormatterFactory<? extends Annotation> annotationFormatterFactory) {
Class<? extends Annotation> annotationType = getAnnotationType(annotationFormatterFactory);
// 若你⾃定义的实现了EmbeddedValueResolverAware接⼝,还可以使⽤占位符哟~~~~
// AnnotationFormatterFactory是下⾯的重点内容~~~~
if (beddedValueResolver != null && annotationFormatterFactory instanceof EmbeddedValueR
esolverAware) {
((EmbeddedValueResolverAware) annotationFormatterFactory).beddedValueResolver);
}
// 对每⼀种字段的type  都注册⼀个AnnotationPrinterConverter去处理~~~~~
// AnnotationPrinterConverter是⼀个ConditionalGenericConverter
// matches⽅法为:sourceType.hasAnnotation(this.annotationType);
// 这个判断是呼应的:因为annotationFormatterFactory只会作⽤在指定的字段类型上的~~~不符合类型条件的不⽤添加
Set<Class<?>> fieldTypes = FieldTypes();
for (Class<?> fieldType : fieldTypes) {
addConverter(new AnnotationPrinterConverter(annotationType, annotationFormatterFactory, fieldTyp
e));
addConverter(new AnnotationParserConverter(annotationType, annotationFormatterFactory, fieldType));
}
}
...
}
AnnotationFormatterFactory 它是⼀个⼯⼚,专门创建出处理(格式化)指定字段field上标注有指定注解的。(Spring内助了两个常⽤注解:@DateTimeFormat和@NumberFormat) 我们常说的,要⾃定义注解来处理参数的格式化,就需要实现接⼝来⾃定义⼀个处理类。
// @since 3.0
public interface AnnotationFormatterFactory<A extends Annotation> {
/
/ 此注解可以作⽤的字段的类型~~~⽐如@DateTimeFormat只能作⽤域Date、Calendar、Long类型上~  标注在被的类型上⽆效~~~ Set<Class<?>> getFieldTypes();
// 对标注有指定注解的字段进⾏格式化输出~~
Printer<?> getPrinter(A annotation, Class<?> fieldType);
// 对标注有指定注解的字段进⾏格式化解析~~~
Parser<?> getParser(A annotation, Class<?> fieldType);
}
NumberFormatAnnotationFormatterFactory
处理@NumberFormat对数字进⾏格式化。
// 还继承⾃EmbeddedValueResolutionSupport,所以有resolveEmbeddedValue()⽅法,能够处理占位符
public class NumberFormatAnnotationFormatterFactory extends EmbeddedValueResolutionSupport
implements AnnotationFormatterFactory<NumberFormat> {
// 处理Byte、Short、Integer、Long、Float、Double、BigInteger、BigDecimal等类型~~~
@Override
public Set<Class<?>> getFieldTypes() {
return NumberUtils.STANDARD_NUMBER_TYPES;
}
@Override
public Printer<Number> getPrinter(NumberFormat annotation, Class<?> fieldType) {
return configureFormatterFrom(annotation);
}
@Override
public Parser<Number> getParser(NumberFormat annotation, Class<?> fieldType) {
return configureFormatterFrom(annotation);
}
// 可以看到,根据Style不同,返回的格式化器也是不同的~~~~
// 显然pattern⾮常强⼤,⽀持到了占位符,el取值~~~
private Formatter<Number> configureFormatterFrom(NumberFormat annotation) {
String pattern = resolveEmbeddedValue(annotation.pattern());
// 若指定了pattern,此处可以看出:直接当做数字处理NumberStyleFormatter
if (StringUtils.hasLength(pattern)) {
bigdecimal格式化两位小数return new NumberStyleFormatter(pattern);
}
/
/ 可能是钱币、百分⽐、数字注意:都是使⽤的默认处理⽅式了~~~~
// @NumberFormat并不⽀持⾃定义⽐如保留⼩数位、四舍五⼊等等
else {
Style style = annotation.style();
if (style == Style.CURRENCY) {
return new CurrencyStyleFormatter();
}
else if (style == Style.PERCENT) {
return new PercentStyleFormatter();
}
else {
return new NumberStyleFormatter();
}
}
}
}
@NumberFormat是⽤来验证输⼊的数字格式。⽐如⼀般我们这样来格式化数值:@NumberFormat(pattern="#,###.##")
@NumberFormat注解内容:
// @since 3.0 类⽐效果参见:NumberFormat
// 可以标注在⽅法上、属性field上、参数上~~~~
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
public @interface NumberFormat {
Style style() default Style.DEFAULT;
// 格式化数字的模版~~~  若指定了pattern 那就使⽤new NumberStyleFormatter(pattern)进⾏格式化
String pattern() default "";
enum Style {
// 默认值同 NUMBER
DEFAULT,
NUMBER,
PERCENT,
CURRENCY
}
}
FormattingConversionServiceFactoryBean
它和上⾯的不同,它是⽤于管理转换器、格式化器们的。⽐如我们⾃⼰⾃定义了⼀个转换器、格式化器需要注册,都以交给它。从名字可以
看出,它主要是创建⼀个FormattingConversionService,⽽它上⾯说了它既还有转换器,⼜有格式化器~~~
public class FormattingConversionServiceFactoryBean implements FactoryBean<FormattingConversionService>, EmbeddedValueResolverAware, InitializingBean
@Nullable
private Set<?> converters;
@Nullable
private Set<?> formatters;
// 由此可见,我们要注册formatter不仅仅可以直接注册,也可通过formatterRegistrars注册进来~
@Nullable
private Set<FormatterRegistrar> formatterRegistrars;
private boolean registerDefaultFormatters = true;
@Nullable
private StringValueResolver embeddedValueResolver;
@Nullable
private FormattingConversionService conversionService; // 最终是它⽤于管理所有备注:所有的formatter最终都是⼀个converter
// 这⾥把上⾯字段set进来的值,进⾏解析~~~~拆分~~~
@Override
public void afterPropertiesSet() {
// 由此可见,最终返回的是⼀个DefaultFormattingConversionService
//  把set进来的这些converters都注册进去保存着~~~
// 这⾥处理注册formatters和formatterRegistrars们~~~~
versionService);
}
private void registerFormatters(FormattingConversionService conversionService) {
if (this.formatters != null) {

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。