上篇文章解析了spring类型转换的接口和他们的分工,怎么通过设计模式实现转换功能。
这篇需要些上篇的知识,如果没有看可以从这儿跳转spring 官方文档的接口理解整理(三)类型转换
一、准备新建Maven项目,pom.xml内容如下
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.boot.version>2.0.4.RELEASE</spring.boot.version>
<spring.version>5.0.8.RELEASE</spring.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring.boot.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
新建AppConfig.java,创建FormattingConversionService对象到ApplicationContext中,代码如下:
@Configuration
public class AppConfig
{
@Bean
public FormattingConversionService conversionService() {
//初始化生成转换服务
FormattingConversionService conversionService = new DefaultFormattingConversionService(false);
//添加数字注解格式化格式化
conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
//日期格式化注册者,注册一大堆普通的日期coverter的实现,所有的都将放到conversionService,
//并且通过委托FormattingConversionService添加GenericConverter接口的实现
DateFormatterRegistrar registrar = new DateFormatterRegistrar();
registrar.setFormatter(new DateFormatter("yyyyMMdd"));
registrar.registerFormatters(conversionService);
return conversionService;
}
}
创建AppTest.java
public class AppTest
{
@Test
public void formatterRegisteryTest(){
ApplicationContext context = new AnnotationConfigApplicationContext(App.class);
FormattingConversionService service = context.getBean(FormattingConversionService.class);
Date date = (Date) service.convert("20180923", TypeDescriptor.valueOf(Date.class));
Date date1 = (Date) service.convert(Calendar.getInstance(), TypeDescriptor.valueOf(Date.class));
System.out.println(date);
System.out.println(date1);
}
}
运行结果:
Sun Sep 23 00:00:00 CST 2018
Tue Sep 25 12:56:45 CST 2018
二、执行中的uml的序列图:
注册DateFormatter和DateConverter的过程:
执行convert方法:
三、源码解析:
AppConfig类的FormattingConversionService conversionService()方法和@Bean是生成一个FormattingConversionService对象存储到spring容器中。
首先分析DefaultFormattingConversionService结构:
DefaultFormattingConversionService实现了FormatterRegistry和ConverionService接口,FormatterRegistry接口提供Formatter和Converter的添加和删除等操作,添加的对象给ConverionService接口用来使用,ConverionService本身不完成convert,它是通过委托给converter和formatter完成的。
1、先来看怎么提供添加和删除等操作的代码
FormattingConversionService.java 部分代码
@Override
public void addFormatter(Formatter<?> formatter) {
addFormatterForFieldType(getFieldType(formatter), formatter);
}
@Override
public void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter) {
addConverter(new PrinterConverter(fieldType, formatter, this));
addConverter(new ParserConverter(fieldType, formatter, this));
}
@Override
public void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser) {
addConverter(new PrinterConverter(fieldType, printer, this));
addConverter(new ParserConverter(fieldType, parser, this));
}
@Override
public void addFormatterForFieldAnnotation(AnnotationFormatterFactory<? extends Annotation> annotationFormatterFactory) {
Class<? extends Annotation> annotationType = getAnnotationType(annotationFormatterFactory);
if (this.embeddedValueResolver != null && annotationFormatterFactory instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) annotationFormatterFactory).setEmbeddedValueResolver(this.embeddedValueResolver);
}
Set<Class<?>> fieldTypes = annotationFormatterFactory.getFieldTypes();
for (Class<?> fieldType : fieldTypes) {
addConverter(new AnnotationPrinterConverter(annotationType, annotationFormatterFactory, fieldType));
addConverter(new AnnotationParserConverter(annotationType, annotationFormatterFactory, fieldType));
}
}
addConverter方法定义在父类GenericConversionService中。
private final Converters converters = new Converters(); //保存所有的格式转换器
@Override
public void addConverter(Converter<?, ?> converter) {
ResolvableType[] typeInfo = getRequiredTypeInfo(converter.getClass(), Converter.class);
if (typeInfo == null && converter instanceof DecoratingProxy) {
typeInfo = getRequiredTypeInfo(((DecoratingProxy) converter).getDecoratedClass(), Converter.class);
}
if (typeInfo == null) {
throw new IllegalArgumentException("Unable to determine source type <S> and target type <T> for your " +
"Converter [" + converter.getClass().getName() + "]; does the class parameterize those types?");
}
addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1]));
}
@Override
public <S, T> void addConverter(Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter) {
addConverter(new ConverterAdapter(
converter, ResolvableType.forClass(sourceType), ResolvableType.forClass(targetType)));
}
@Override
public void addConverter(GenericConverter converter) {
this.converters.add(converter);
invalidateCache();
}
/**
* 保存并且管理所有的注册的对象
*/
private static class Converters {
private final Set<GenericConverter> globalConverters = new LinkedHashSet<>();//保存全局的Converter
private final Map<ConvertiblePair, ConvertersForPair> converters = new LinkedHashMap<>(36);//保存往返fieldType组合的ConvertiblePair和Converter
public void add(GenericConverter converter) {
Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes();
if (convertibleTypes == null) {
Assert.state(converter instanceof ConditionalConverter,
"Only conditional converters may return null convertible types");
this.globalConverters.add(converter);
}
else {
for (ConvertiblePair convertiblePair : convertibleTypes) {
ConvertersForPair convertersForPair = getMatchableConverters(convertiblePair);
convertersForPair.add(converter);
}
}
}
//通过Convertiblepair查找适合的Converter
private ConvertersForPair getMatchableConverters(ConvertiblePair convertiblePair) {
ConvertersForPair convertersForPair = this.converters.get(convertiblePair);
if (convertersForPair == null) {
convertersForPair = new ConvertersForPair();
this.converters.put(convertiblePair, convertersForPair);
}
return convertersForPair;
}
public void remove(Class<?> sourceType, Class<?> targetType) {
this.converters.remove(new ConvertiblePair(sourceType, targetType));
}
/**
通过TypeDescriptor sourceType, TypeDescriptor targetType查找适合的Converter
*/
@Nullable
public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) {
// Search the full type hierarchy
List<Class<?>> sourceCandidates = getClassHierarchy(sourceType.getType());
List<Class<?>> targetCandidates = getClassHierarchy(targetType.getType());
for (Class<?> sourceCandidate : sourceCandidates) {
for (Class<?> targetCandidate : targetCandidates) {
ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair);
if (converter != null) {
return converter;
}
}
}
return null;
}
@Nullable
private GenericConverter getRegisteredConverter(TypeDescriptor sourceType,
TypeDescriptor targetType, ConvertiblePair convertiblePair) {
// Check specifically registered converters
ConvertersForPair convertersForPair = this.converters.get(convertiblePair);
if (convertersForPair != null) {
GenericConverter converter = convertersForPair.getConverter(sourceType, targetType);
if (converter != null) {
return converter;
}
}
// Check ConditionalConverters for a dynamic match
for (GenericConverter globalConverter : this.globalConverters) {
if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) {
return globalConverter;
}
}
return null;
}
/**
* Returns an ordered class hierarchy for the given type.
* @param type the type
* @return an ordered list of all classes that the given type extends or implements
*/
private List<Class<?>> getClassHierarchy(Class<?> type) {
List<Class<?>> hierarchy = new ArrayList<>(20);
Set<Class<?>> visited = new HashSet<>(20);
addToClassHierarchy(0, ClassUtils.resolvePrimitiveIfNecessary(type), false, hierarchy, visited);
boolean array = type.isArray();
int i = 0;
while (i < hierarchy.size()) {
Class<?> candidate = hierarchy.get(i);
candidate = (array ? candidate.getComponentType() : ClassUtils.resolvePrimitiveIfNecessary(candidate));
Class<?> superclass = candidate.getSuperclass();
if (superclass != null && superclass != Object.class && superclass != Enum.class) {
addToClassHierarchy(i + 1, candidate.getSuperclass(), array, hierarchy, visited);
}
addInterfacesToClassHierarchy(candidate, array, hierarchy, visited);
i++;
}
if (Enum.class.isAssignableFrom(type)) {
addToClassHierarchy(hierarchy.size(), Enum.class, array, hierarchy, visited);
addToClassHierarchy(hierarchy.size(), Enum.class, false, hierarchy, visited);
addInterfacesToClassHierarchy(Enum.class, array, hierarchy, visited);
}
addToClassHierarchy(hierarchy.size(), Object.class, array, hierarchy, visited);
addToClassHierarchy(hierarchy.size(), Object.class, false, hierarchy, visited);
return hierarchy;
}
private void addInterfacesToClassHierarchy(Class<?> type, boolean asArray,
List<Class<?>> hierarchy, Set<Class<?>> visited) {
for (Class<?> implementedInterface : type.getInterfaces()) {
addToClassHierarchy(hierarchy.size(), implementedInterface, asArray, hierarchy, visited);
}
}
private void addToClassHierarchy(int index, Class<?> type, boolean asArray,
List<Class<?>> hierarchy, Set<Class<?>> visited) {
if (asArray) {
type = Array.newInstance(type, 0).getClass();
}
if (visited.add(type)) {
hierarchy.add(index, type);
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("ConversionService converters =\n");
for (String converterString : getConverterStrings()) {
builder.append('\t').append(converterString).append('\n');
}
return builder.toString();
}
private List<String> getConverterStrings() {
List<String> converterStrings = new ArrayList<>();
for (ConvertersForPair convertersForPair : converters.values()) {
converterStrings.add(convertersForPair.toString());
}
Collections.sort(converterStrings);
return converterStrings;
}
}
PrinterConverter和ParserConverter是为了包装Formatter接口中的Printer和Parser接口。
private static class PrinterConverter implements GenericConverter {
private final Class<?> fieldType;
private final TypeDescriptor printerObjectType;
@SuppressWarnings("rawtypes")
private final Printer printer;
private final ConversionService conversionService;
public PrinterConverter(Class<?> fieldType, Printer<?> printer, ConversionService conversionService) {
this.fieldType = fieldType;
this.printerObjectType = TypeDescriptor.valueOf(resolvePrinterObjectType(printer));
this.printer = printer;
this.conversionService = conversionService;
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(this.fieldType, String.class));
}
@Override
@SuppressWarnings("unchecked")
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (!sourceType.isAssignableTo(this.printerObjectType)) {
source = this.conversionService.convert(source, sourceType, this.printerObjectType);
}
if (source == null) {
return "";
}
return this.printer.print(source, LocaleContextHolder.getLocale());
}
@Nullable
private Class<?> resolvePrinterObjectType(Printer<?> printer) {
return GenericTypeResolver.resolveTypeArgument(printer.getClass(), Printer.class);
}
@Override
public String toString() {
return (this.fieldType.getName() + " -> " + String.class.getName() + " : " + this.printer);
}
}
private static class ParserConverter implements GenericConverter {
private final Class<?> fieldType;
private final Parser<?> parser;
private final ConversionService conversionService;
public ParserConverter(Class<?> fieldType, Parser<?> parser, ConversionService conversionService) {
this.fieldType = fieldType;
this.parser = parser;
this.conversionService = conversionService;
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, this.fieldType));
}
@Override
@Nullable
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
String text = (String) source;
if (!StringUtils.hasText(text)) {
return null;
}
Object result;
try {
result = this.parser.parse(text, LocaleContextHolder.getLocale());
}
catch (IllegalArgumentException ex) {
throw ex;
}
catch (Throwable ex) {
throw new IllegalArgumentException("Parse attempt failed for value [" + text + "]", ex);
}
TypeDescriptor resultType = TypeDescriptor.valueOf(result.getClass());
if (!resultType.isAssignableTo(targetType)) {
result = this.conversionService.convert(result, resultType, targetType);
}
return result;
}
@Override
public String toString() {
return (String.class.getName() + " -> " + this.fieldType.getName() + ": " + this.parser);
}
}
接口FormatterRegistry调用是通过FormatterRegistrar接口调用的,FormatterRegistrar的调用正是AppConfig中。
DateFormatterRegistrar registrar = new DateFormatterRegistrar();
registrar.setFormatter(new DateFormatter("yyyyMMdd"));
registrar.registerFormatters(conversionService);
这儿来看DateFormatterRegistrar的源码:
DateFormatterRegistrar.java
public class DateFormatterRegistrar implements FormatterRegistrar {
@Nullable
private DateFormatter dateFormatter;
public void setFormatter(DateFormatter dateFormatter) {
Assert.notNull(dateFormatter, "DateFormatter must not be null");
this.dateFormatter = dateFormatter;
}
@Override
public void registerFormatters(FormatterRegistry registry) {
addDateConverters(registry);
registry.addFormatterForFieldAnnotation(new DateTimeFormatAnnotationFormatterFactory());
// In order to retain back compatibility we only register Date/Calendar
// types when a user defined formatter is specified (see SPR-10105)
if (this.dateFormatter != null) {
registry.addFormatter(this.dateFormatter);
registry.addFormatterForFieldType(Calendar.class, this.dateFormatter);
}
}
public static void addDateConverters(ConverterRegistry converterRegistry) {
converterRegistry.addConverter(new DateToLongConverter());
converterRegistry.addConverter(new DateToCalendarConverter());
converterRegistry.addConverter(new CalendarToDateConverter());
converterRegistry.addConverter(new CalendarToLongConverter());
converterRegistry.addConverter(new LongToDateConverter());
converterRegistry.addConverter(new LongToCalendarConverter());
}
private static class DateToLongConverter implements Converter<Date, Long> {
@Override
public Long convert(Date source) {
return source.getTime();
}
}
private static class DateToCalendarConverter implements Converter<Date, Calendar> {
@Override
public Calendar convert(Date source) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(source);
return calendar;
}
}
private static class CalendarToDateConverter implements Converter<Calendar, Date> {
@Override
public Date convert(Calendar source) {
return source.getTime();
}
}
private static class CalendarToLongConverter implements Converter<Calendar, Long> {
@Override
public Long convert(Calendar source) {
return source.getTimeInMillis();
}
}
private static class LongToDateConverter implements Converter<Long, Date> {
@Override
public Date convert(Long source) {
return new Date(source);
}
}
private static class LongToCalendarConverter implements Converter<Long, Calendar> {
@Override
public Calendar convert(Long source) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(source);
return calendar;
}
}
}
从上面可以看出addDateConverters方法是为了注册Converter的实现,调用的是GenericConversionService类的addConverter(),
添加完普通的再开始添加GenericConverter接口的实现,这儿是委托FormattingConversionService通过FormatterRegistry接口的方法添加Printer和Parser接口的包装成Converter,Converter接口的convert方法的实现是通过委托给Printer和Parser接口的Print和Parse方法。