首页 > 编程语言 >Spring 类型转换详解,SpringBean创建时属性类型转换源码详解

Spring 类型转换详解,SpringBean创建时属性类型转换源码详解

时间:2023-04-03 20:42:21浏览次数:43  
标签:类型转换 String PropertyEditor 接口 详解 springframework org 源码


文章目录

  • 一、概述
  • 1、Spring 类型转换的实现
  • 2、使用场景
  • 3、源码分析
  • 二、基于 JavaBeans 接口的类型转换
  • 1、代码实例
  • 2、Spring 內建 PropertyEditor 扩展
  • ByteArrayPropertyEditor
  • 3、自定义 PropertyEditor 扩展整合到springframework
  • 代码实例
  • Spring PropertyEditor 的设计缺陷
  • 三、Spring 3 通用类型转换接口
  • 1、初识Converter、GenericConverter接口
  • 2、Spring 內建类型转换器
  • 3、Converter 接口的局限性及解决方案
  • 局限一:缺少 Source Type 和 Target Type 前置判断
  • 局限二:仅能转换单一的 Source Type 和 Target Type
  • 4、GenericConverter 接口详解
  • GenericConverter接口的局限性
  • GenericConverter 优化接口 - ConditionalGenericConverter接口
  • 案例分析
  • 5、扩展 Spring 类型转换器
  • 代码实例
  • 6、内置统一类型转换服务-ConversionService
  • tips1
  • tips2
  • 7、ConversionService 作为依赖
  • TypeConverter
  • TypeConverterSupport
  • TypeConverterDelegate
  • 整体流程
  • 参考资料

一、概述

本文主要内容是Spring Bean通过xml等配置进行属性映射时,发生的类型转换的基本原理与源码分析。

1、Spring 类型转换的实现

Spring 3.0以前,基于 JavaBeans 接口的类型转换实现,基于 java.beans.PropertyEditor 接口扩展。

Spring 3.0+ 通用类型转换实现。

2、使用场景

场景

基于 JavaBeans 接口的类型转换实现

Spring 3.0+ 通用类型转换实现

数据绑定

YES

YES

BeanWrapper

YES

YES

Bean 属性类型装换

YES

YES

外部化属性类型转换

NO

YES

3、源码分析

我们之前提到过:Spring数据绑定详解,Spring-Data Binding源码分析

数据绑定通过DataBinder进行绑定的,而DataBinder类实现了PropertyEditorRegistry和TypeConverter。

PropertyEditorRegistry接口和PropertyEditor 有着直接的关系:

public interface PropertyEditorRegistry {

	void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);

	void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor);

	@Nullable
	PropertyEditor findCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath);

}

BeanWrapper接口继承了ConfigurablePropertyAccessor接口,而ConfigurablePropertyAccessor接口提供了对ConversionService(类型转换服务)的操作,同时有个开关,设置是否用PropertyEditor抽取老的值。

上面了解了一些关于类型转换的接口后,我们继续回到DefaultListableBeanFactory的父类AbstractAutowireCapableBeanFactory的doCreateBean方法,同样用到了BeanWrapper,在调用populateBean时,会执行applyPropertyValues方法,这与DataBinder的bind方法中的applyPropertyValues方法的实现基本是一致的。

applyPropertyValues方法中就涉及到了我们本文的重点-Bean属性绑定时发生的类型转换。

二、基于 JavaBeans 接口的类型转换

核心职责:将 String 类型的内容转化为目标类型的对象

扩展原理:
• Spring 框架将文本内容传递到 PropertyEditor 实现的 setAsText(String) 方法
• PropertyEditor#setAsText(String) 方法实现将 String 类型转化为目标类型的对象
• 将目标类型的对象传入 PropertyEditor#setValue(Object) 方法
• PropertyEditor#setValue(Object) 方法实现需要临时存储传入对象
• Spring 框架将通过 PropertyEditor#getValue() 获取类型转换后的对象

1、代码实例

import java.beans.PropertyEditor;
import java.beans.PropertyEditorSupport;
import java.io.IOException;
import java.io.StringReader;
import java.util.Map;
import java.util.Properties;

/**
 * String -> Properties {@link PropertyEditor}
 */
public class StringToPropertiesPropertyEditor extends PropertyEditorSupport implements PropertyEditor {

    // 1. 实现 setAsText(String) 方法
    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        // 2. 将 String 类型转换成 Properties 类型
        Properties properties = new Properties();
        try {
            properties.load(new StringReader(text));
        } catch (IOException e) {
            throw new IllegalArgumentException(e);
        }

        // 3. 临时存储 Properties 对象
        setValue(properties);

        // 接下来要 获取临时 Properties 对象 -通过getValue();
    }

    @Override
    public String getAsText() {
        Properties properties = (Properties) getValue();

        StringBuilder textBuilder = new StringBuilder();

        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
            textBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append(System.getProperty("line.separator")); // 换行符
        }

        return textBuilder.toString();
    }
}
// 模拟 Spring Framework 操作
// 有一段文本 name = 张三;
String text = "name = 张三";

PropertyEditor propertyEditor = new StringToPropertiesPropertyEditor();
// 传递 String 类型的内容
propertyEditor.setAsText(text);

System.out.println(propertyEditor.getValue());

System.out.println(propertyEditor.getAsText());

2、Spring 內建 PropertyEditor 扩展

內建扩展(org.springframework.beans.propertyeditors 包下)(仅限于String转换为其他类型)

转换场景

实现类

String -> Byte 数组

org.springframework.beans.propertyeditors.ByteArrayPropertyEditor

String -> Char

org.springframework.beans.propertyeditors.CharacterEditor

String -> Char 数组

org.springframework.beans.propertyeditors.CharArrayPropertyEditor

String -> Charset

org.springframework.beans.propertyeditors.CharsetEditor

String -> Class

org.springframework.beans.propertyeditors.ClassEditor

String -> Currency

org.springframework.beans.propertyeditors.CurrencyEditor



ByteArrayPropertyEditor

我们可以看出,基本也都是继承了PropertyEditorSupport ,实现了setAsText和getAsText方法。其他转换器都类似,我们上面的代码实例也是这样实现的。

public class ByteArrayPropertyEditor extends PropertyEditorSupport {

	@Override
	public void setAsText(@Nullable String text) {
		setValue(text != null ? text.getBytes() : null);
	}

	@Override
	public String getAsText() {
		byte[] value = (byte[]) getValue();
		return (value != null ? new String(value) : "");
	}

}

3、自定义 PropertyEditor 扩展整合到springframework

扩展模式: 扩展 java.beans.PropertyEditorSupport 类

实现 org.springframework.beans.PropertyEditorRegistrar
• 实现 registerCustomEditors(org.springframework.beans.PropertyEditorRegistry) 方法
• 将 PropertyEditorRegistrar 实现注册为 Spring Bean

向 org.springframework.beans.PropertyEditorRegistry 注册自定义 PropertyEditor 实现
• 通用类型实现 registerCustomEditor(Class<?>, PropertyEditor) • Java Bean 属性类型实现:registerCustomEditor(Class<?>, String, PropertyEditor)

代码实例

我们复用上面的StringToPropertiesPropertyEditor。

import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.stereotype.Component;

/**
 * 自定义 {@link PropertyEditorRegistrar} 实现
 *
 * @see PropertyEditorRegistrar
 */
// @Component // 3. 将其声明为 Spring Bean
public class CustomizedPropertyEditorRegistrar implements PropertyEditorRegistrar {

    @Override
    public void registerCustomEditors(PropertyEditorRegistry registry) {
        // 1. 通用类型转换
        // 2. Java Bean 属性类型转换
        registry.registerCustomEditor(Properties.class, "context", new StringToPropertiesPropertyEditor());
    }
}
<bean id="customEditorConfigurer" class="com.test.config.CustomEditorConfigurer">
 <property name="propertyEditorRegistrars">
        <list>
            <bean class="com.test.conversion.CustomizedPropertyEditorRegistrar"/>
        </list>
    </property>
</bean>

<bean id="user" class="com.test.domain.User">
    <property name="id" value="1"/>
    <property name="name" value="张三"/>
    <property name="context"> <!-- Properties 类型 -->
        <value>
            id = 1
            name = zhangsan
        </value>
    </property>
    <property name="contextAsText" ref="context"/> <!-- Properties -> String -->
</bean>
private Properties context;
private String contextAsText;

Spring PropertyEditor 的设计缺陷

Spring3之后弃用了PropertyEditor ,PropertyEditor 的设计缺陷主要有以下三种:

1、违反职责单一原则
• java.beans.PropertyEditor 接口职责太多,除了类型转换,还包括 Java Beans 事件和 Java GUI 交互

2、java.beans.PropertyEditor 实现类型局限
• 来源类型只能为 java.lang.String 类型

3、java.beans.PropertyEditor 实现缺少类型安全
• 除了实现类命名可以表达语义,实现类无法感知目标转换类型

三、Spring 3 通用类型转换接口

1、初识Converter、GenericConverter接口

类型转换接口 - org.springframework.core.convert.converter.Converter<S,T>
• 泛型参数 S:来源类型,参数 T:目标类型
• 核心方法:T convert(S)

通用类型转换接口 - org.springframework.core.convert.converter.GenericConverter
• 核心方法:convert(Object,TypeDescriptor,TypeDescriptor)
• 配对类型:org.springframework.core.convert.converter.GenericConverter.ConvertiblePair
• 类型描述:org.springframework.core.convert.TypeDescriptor

我们看一下Converter接口,泛型S、T分别代表来源和目标类型,这个接口的实现是线程安全的,并且可以共享:

@FunctionalInterface
public interface Converter<S, T> {
	@Nullable
	T convert(S source);
}

我们再看一下ConditionalConverter接口,Converter接口尽管强大但是仍有不足,增加了一个有条件的接口:

public interface ConditionalConverter {
	// 转换之前,预判输入类型和输出类型
	boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}

TypeDescriptor 就是类型描述,里面包含了许多类型的信息。

我们再来看一下通用类型转换接口-GenericConverter接口,使用ConvertiblePair包装了一个Set,可以支持多种类型:

public interface GenericConverter {

	@Nullable
	Set<ConvertiblePair> getConvertibleTypes();

	@Nullable
	Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);


	/**
	 * Holder for a source-to-target class pair.
	 */
	final class ConvertiblePair {

		private final Class<?> sourceType;

		private final Class<?> targetType;

		/**
		 * Create a new source-to-target pair.
		 * @param sourceType the source type
		 * @param targetType the target type
		 */
		public ConvertiblePair(Class<?> sourceType, Class<?> targetType) {
			Assert.notNull(sourceType, "Source type must not be null");
			Assert.notNull(targetType, "Target type must not be null");
			this.sourceType = sourceType;
			this.targetType = targetType;
		}

		public Class<?> getSourceType() {
			return this.sourceType;
		}

		public Class<?> getTargetType() {
			return this.targetType;
		}

		@Override
		public boolean equals(@Nullable Object other) {
			if (this == other) {
				return true;
			}
			if (other == null || other.getClass() != ConvertiblePair.class) {
				return false;
			}
			ConvertiblePair otherPair = (ConvertiblePair) other;
			return (this.sourceType == otherPair.sourceType && this.targetType == otherPair.targetType);
		}

		@Override
		public int hashCode() {
			return (this.sourceType.hashCode() * 31 + this.targetType.hashCode());
		}

		@Override
		public String toString() {
			return (this.sourceType.getName() + " -> " + this.targetType.getName());
		}
	}

}

2、Spring 內建类型转换器

转换场景

实现类所在包名(package)

日期/时间相关

org.springframework.format.datetime

Java 8 日期/时间相关

org.springframework.format.datetime.standard

通用实现

org.springframework.core.convert.support

通过这些源码来看,使用方式与上面的PropertyEditor方式有着较大的差别(篇幅限制,大家自行翻阅源码)。

3、Converter 接口的局限性及解决方案

局限一:缺少 Source Type 和 Target Type 前置判断

Converter 只提供了一个T convert(S source);方法,输入一个参数返回一个参数,没有判断是否支持这个参数类型的转换。

应对:增加 org.springframework.core.convert.converter.ConditionalConverter 实现,同时实现Converter、ConditionalConverter 接口,其中ConditionalConverter 接口的matches方法可以判断是否支持该类型参数的转换。

局限二:仅能转换单一的 Source Type 和 Target Type

Converter 只提供了一个T convert(S source);方法,输入一个参数返回一个参数,对集合类型的参数转换不友好。

应对:使用 org.springframework.core.convert.converter.GenericConverter 代替,GenericConverter 接口我们上面也分析到,用一个内部类ConvertiblePair可以实现集合类型转换。

实际上,Converter和GenericConverter 会相互配合,简单类型的转换一般使用Converter,复合类型的转换一般使用GenericConverter

4、GenericConverter 接口详解

org.springframework.core.convert.converter.GenericConverter接口,是一个通用的类型转换接口,它比Converter适用性更广。

核心要素

说明

使用场景

主要用于“复合”类型转换场景,比如 Collection、Map、数组等

转换范围

Set<ConvertiblePair> getConvertibleTypes()

配对类型

org.springframework.core.convert.converter.GenericConverter.ConvertiblePair

转换方法

convert(Object,TypeDescriptor,TypeDescriptor)

类型描述

org.springframework.core.convert.TypeDescriptor

GenericConverter接口的局限性

与Converter接口一样,GenericConverter接口也缺少 Source Type 和 Target Type 前置判断。

同时,GenericConverter接口单一类型转换实现复杂,也就是说,GenericConverter比较适合复合类型的转换,Converter接口比较适合单一类型的转换。

GenericConverter 优化接口 - ConditionalGenericConverter接口

ConditionalGenericConverter接口整合了GenericConverter接口和ConditionalConverter接口,解决了GenericConverter接口缺少 Source Type 和 Target Type 前置判断的问题。

public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {

}

案例分析

我们分析一下StringToCollectionConverter转换类(其他大量转换类同理):

final class StringToCollectionConverter implements ConditionalGenericConverter {

	private final ConversionService conversionService; // 通过ConversionService 来进行处理


	public StringToCollectionConverter(ConversionService conversionService) {
		this.conversionService = conversionService;
	}


	@Override
	public Set<ConvertiblePair> getConvertibleTypes() {
		return Collections.singleton(new ConvertiblePair(String.class, Collection.class));
	}

	@Override
	public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
		 // 通过ConversionService 来进行处理,判断类型是否匹配
		return (targetType.getElementTypeDescriptor() == null ||
				this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor()));
	}

	@Override
	@Nullable
	public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
		if (source == null) {
			return null;
		}
		String string = (String) source;

		String[] fields = StringUtils.commaDelimitedListToStringArray(string);
		TypeDescriptor elementDesc = targetType.getElementTypeDescriptor();
		Collection<Object> target = CollectionFactory.createCollection(targetType.getType(),
				(elementDesc != null ? elementDesc.getType() : null), fields.length);

		if (elementDesc == null) {
			for (String field : fields) {
				target.add(field.trim());
			}
		}
		else {
			for (String field : fields) {
				Object targetElement = this.conversionService.convert(field.trim(), sourceType, elementDesc);
				target.add(targetElement);
			}
		}
		return target;
	}

}

5、扩展 Spring 类型转换器

实现转换器接口
• org.springframework.core.convert.converter.Converter
• org.springframework.core.convert.converter.ConverterFactory
• org.springframework.core.convert.converter.GenericConverter

注册转换器实现
• 通过 ConversionServiceFactoryBean Spring Bean
• 通过 org.springframework.core.convert.ConversionService API

代码实例

import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;

import java.util.Collections;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

/**
 * {@link Properties} -> {@link String} {@link ConditionalGenericConverter} 实现
 *
 * @see Properties
 * @see ConditionalGenericConverter
 */
public class PropertiesToStringConverter implements ConditionalGenericConverter {

    @Override
    public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
        return Properties.class.equals(sourceType.getObjectType())
                && String.class.equals(targetType.getObjectType());
    }

    @Override
    public Set<ConvertiblePair> getConvertibleTypes() {
        return Collections.singleton(new ConvertiblePair(Properties.class, String.class));
    }

    @Override
    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {

        Properties properties = (Properties) source;

        StringBuilder textBuilder = new StringBuilder();

        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
            textBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append(System.getProperty("line.separator"));
        }

        return textBuilder.toString();
    }
}
<!-- 声明 ConversionServiceFactoryBean 并且 name 必须为 "conversionService" -->
<bean id="conversionService" class="com.test.ConversionServiceFactoryBean">
    <property name="converters">
        <bean class="com.test.PropertiesToStringConverter"/>
    </property>
</bean>

<!-- java.util.Properties -->
<util:properties id="context">
    <prop key="id">1</prop>
    <prop key="name">zhangsan</prop>
</util:properties>

<bean id="user" class="com.test.domain.User">
    <property name="contextAsText" ref="context"/> <!-- Properties -> String -->
</bean>

6、内置统一类型转换服务-ConversionService

通过类型转换的源码我们看到,类型转换都需要使用org.springframework.core.convert.ConversionService类型转换服务。

实现类型

说明

GenericConversionService

通用 ConversionService 模板实现,不内置转化器实现

DefaultConversionService

基础 ConversionService 实现,内置常用转化器实现

FormattingConversionService

通用 Formatter + GenericConversionService 实现,不内置转化器和Formatter 实现

DefaultFormattingConversionService

DefaultConversionService + 格式化 实现(如:JSR-354 Money & Currency, JSR-310 Date-Time)

以上是Spring自带的类型转换服务,Springboot中提供了WebConversionService继承了DefaultFormattingConversionService,在这个基础上做了一些扩展。

tips1

在xml文件中注册ConversionServiceFactoryBean,此类中组合了GenericConversionService(实际上是DefaultConversionService),该类作为Bean定义时,名称一定要是conversionService。

在AbstractApplicationContext#finishBeanFactoryInitialization中,beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
},
此处会以CONVERSION_SERVICE_BEAN_NAME(conversionService)作为bean名称,ConversionService.class作为类型传进去,出于好奇,想对比一下xml文件中定义的conversionService和beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class))获取到的是不是同一个对象,结果发现applicationContext.getBean(“conversionService”)拿到的居然不是ConversionServiceFactoryBean,而是其组合的DefaultConversionService,通过跟踪getBean发现,在AbstractBeanFactory#getObjectForBeanInstance中会判断该对象是否为FactoryBean,如果是,则在FactoryBeanRegistrySupport#doGetObjectFromFactoryBean中通过factory.getObject()返回,返回的正是ConversionServiceFactoryBean中组合的DefaultConversionService。

因此beanFactory.setConversionService中传入的就是ConversionServiceFactoryBean中组合的DefaultConversionService,在doCreatBean中,会去获取BeanWrapper实例(BeanWrapperImpl),实际上最终是通过
protected void initBeanWrapper(BeanWrapper bw) {
bw.setConversionService(getConversionService());
registerCustomEditors(bw);
}
设置conversionService对象。

综上所述,conversionService实际是ConversionServiceFactoryBean中组合的GenericConversionService贯穿了整个上下文。

tips2

那么GenericConversionService是在什么时候被实例化的呢?

不难发现,ConversionServiceFactoryBean实现了InitializingBean接口,在回调方法afterPropertiesSet中实例化:
this.conversionService = createConversionService();
protected GenericConversionService createConversionService() {
return new DefaultConversionService();
}
构造GenericConversionService的过程中,注册了一些诸如:ByteBufferConverter、StringToTimeZoneConverter、ZoneIdToTimeZoneConverterZonedDateTimeToCalendarConverter、ObjectToObjectConverter、IdToEntityConverter、FallbackObjectToStringConverter、ObjectToOptionalConverter的转换器,然后通过ConversionServiceFactory.registerConverters(this.converters, this.conversionService),将自定义的转换器注册到conversionService中。

7、ConversionService 作为依赖

类型转换器底层接口 - org.springframework.beans.TypeConverter
• 起始版本:Spring 2.0
• 核心方法 - convertIfNecessary 重载方法
• 抽象实现 - org.springframework.beans.TypeConverterSupport
• 简单实现 - org.springframework.beans.SimpleTypeConverter

类型转换器底层抽象实现 - org.springframework.beans.TypeConverterSupport
• 实现接口 - org.springframework.beans.TypeConverter
• 扩展实现 - org.springframework.beans.PropertyEditorRegistrySupport
• 委派实现 - org.springframework.beans.TypeConverterDelegate

类型转换器底层委派实现 - org.springframework.beans.TypeConverterDelegate
• 构造来源 - org.springframework.beans.AbstractNestablePropertyAccessor 实现:org.springframework.beans.BeanWrapperImpl
• 依赖 - java.beans.PropertyEditor 实现:默认內建实现 - PropertyEditorRegistrySupport#registerDefaultEditors
• 可选依赖 - org.springframework.core.convert.ConversionService 实现

TypeConverter

TypeConverter接口在Spring2.0时就已经存在了。

TypeConverter接口有三个重载方法:convertIfNecessary,顾名思义,就是如果能转的时候就会转换。

TypeConverterSupport

TypeConverterSupport抽象类继承了PropertyEditorRegistrySupport,实现了TypeConverter。

// org.springframework.beans.TypeConverterSupport#convertIfNecessary(java.lang.Object, java.lang.Class<T>, org.springframework.core.convert.TypeDescriptor)
@Nullable
@Override
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
		@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {

	Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
	try {
		return this.typeConverterDelegate.convertIfNecessary(null, null, value, requiredType, typeDescriptor);
	}
	catch (ConverterNotFoundException | IllegalStateException ex) {
		throw new ConversionNotSupportedException(value, requiredType, ex);
	}
	catch (ConversionException | IllegalArgumentException ex) {
		throw new TypeMismatchException(value, requiredType, ex);
	}
}

通过以上源码可以看出,它会将类型转换委派给TypeConverterDelegate,调用其convertIfNecessary方法。

TypeConverterDelegate

TypeConverterDelegate的核心方法convertIfNecessary就是转换逻辑,先调用ConversionService的canConvert判断能否转换,然后调用convert方法进行转换。

TypeConverterDelegate在AbstractNestablePropertyAccessor的构造方法中new了一个出来,而AbstractNestablePropertyAccessor有一个实现类,就是BeanWrapperImpl。

也就是说我们的BeanWrapper在创建的时候,就关联了一个TypeConverterDelegate,也就是关联了一个类型转换服务。

整体流程

AbstractApplicationContext -> finishBeanFactoryInitialization方法
-> 获取beanName为"conversionService" 的ConversionService Bean
-> ConfigurableBeanFactory#setConversionService(ConversionService)
-> AbstractAutowireCapableBeanFactory#doCreateBean
-> 调用createBeanInstance方法,在instantiateBean方法中,会new BeanWrapperImpl
-> 调用initBeanWrapper方法,set了conversionService
-> AbstractAutowireCapableBeanFactory.instantiateBean
-> AbstractBeanFactory#getConversionService ->

BeanDefinition -> BeanWrapper -> 属性转换(数据来源:PropertyValues)->
setPropertyValues(PropertyValues) -> TypeConverter#convertIfNecessnary ->
TypeConverterDelegate#convertIfNecessnary -> PropertyEditor or ConversionService

参考资料

极客时间-《小马哥讲 Spring 核心编程思想》


标签:类型转换,String,PropertyEditor,接口,详解,springframework,org,源码
From: https://blog.51cto.com/u_13540373/6167350

相关文章

  • Go mod包依赖管理工具使用详解
    我个人觉得,一个包管理工具应该有以下功能:基本功能依赖管理依赖包版本控制对应的包管理平台可以私有化部署加分:代码包是否可以复用构建,测试,打包发布上线对比上面几点:目前做的最好的也就maven了,gradle没有使用过,不知道。今天主角是gomod,先来谈谈没有使用gomod之前的问题。使......
  • MA35D1记录1-源码编译
    今天年假结束,突然发现新唐即将发布MA35D1,去官网和git仓库查了下,新唐趁我放假又偷偷更新了一些资料。之前发布的是yocto的环境,那个我倒也用,但时不时要翻墙,对国内用户来说,多少有点恶心人,今天再去看,终于单独发出linux部分的源码。趁着工作任务不是很重,简单试一下1.源码下载在官网仓......
  • S5PV210开发 -- UART 详解
    上一篇文章系统的讲了一下通信的分类,包括并行通信,串行通信。串行通信的分类,包括同步通信,异步通信。这篇文章我们主要讲一下UART 串口编程,我们并不陌生。之前讲过RS485通信,参看:UNIX再学习--RS485串口编程再者,参看:日常生活小技巧--UART回环测试一、基本概念 参看:UART--维......
  • LIVE555再学习 -- testOnDemandRTSPServer 源码分析
    一、简介先看一下官网上的介绍:testOnDemandRTSPServer createsaRTSPserverthatcanstream,viaRTPunicast,fromvarioustypesofmediafile,ondemand.(Supportedmediatypesinclude:MPEG-1or2audioorvideo(elementarystream),includingMP3audio;MPEG-4......
  • LIVE555再学习 -- testH264VideoStreamer 源码分析
    上一篇文章我们已经讲了一部分:testH264VideoStreamer重复从H.264基本流视频文件(名为“test.264”)中读取,并使用RTP多播进行流式传输。 该程序还具有内置的RTSP服务器。Apple的“QuickTime播放器”可用于接收和播放此音频流。要使用它,让玩家打开会话的“rtsp://”URL(程序在......
  • LIVE555再学习 -- testRTSPClient 源码分析
    现在开讲 testRTSPClient。在官网这这样一段介绍,参看:RTSPclient翻译下来就是:testRTSPClient是一个命令行程序,显示如何打开和接收由RTSPURL指定的媒体流,即以rtsp://开头的URL在这个演示应用中,接收到的音频/视频数据什么也没有。但是,您可以在自己的应用程序中使用和调整此代码(......
  • centos8 源码安装编译 swoole4.6.7
      下载安装包 https://pecl.php.net/package/swoole    解压安装包解压swoole安装包tarzxvfswoole-4.6.7.tgz-C/usr/local/src/进入swoole文件cd/usr/local/src/swoole-4.6.7#phpize编译生成configure/usr/local/php/bin/phpize##编译swoole并生成......
  • UWB定位系统源码,工厂人员轨迹定位系统源码
    UWB高精度定位系统源码,智慧工厂人员定位系统源码,基于Vue+Springboot前后端分离架构开发的一套UWB定位系统源码。有演示。随着经济的高速发展,现代制造业规模不断扩大,生产车间面积广阔,生产设备日益繁多,生产工人数量多且分散作业,难以进行有效管理和实施全方位风险管控。现代工厂安全......
  • flask 请求与响应,session使用与源码分析,闪现与请求扩展
    目录cbv分析总结模板请求与响应请求:全局的request响应:新手四件套session使用及源码分析cookie与sessionsession执行原理session源码分析闪现作用用法请求扩展cbv分析基于类的视图cbv写法fromflaskimportFlask,request#视图基类fromflask.viewsimportMethodViewa......
  • PHPExcel 中文使用手册详解
     1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848......