Spring 类型转换、数值绑定与验证(二)—PropertyEditor与Conversion

本文主要是介绍Spring 类型转换、数值绑定与验证(二)—PropertyEditor与Conversion,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 Spring 中,属性类型转换是在将数值绑定到目标对象时完成的。例如在创建ApplicationContext 容器时,将XML配置的bean 转换成Java类型对象,主要是借助了PropertyEditor类,而在Spring MVC 的Controller的请求参数转化为特定类型时,我们也可以自定义转化器Convert并注册来完成转换。以下是Spring相关源码分析。

1 PropertyEditor

JDK自带的接口。支持各种不同的方式来显示和更新特性值。
xml 配置Bean 或者@Value 赋值到Bean的时候,属性字段值很多是文本类型的字符串,但是属性的类型却可能是Integer,File等类型,PropertyEditor 就是用来把文本型的值转换为对应类型值的工具。 其关键方法是void setAsTest(String text)。

PropertyEditorSupport 是JDK提供的默认自定义,各种自定义的PropertyEditor大多是继承该类来实现。重写setAsText方法。

1.1 PropertyEditorRegistry

PropertyEditor 注册表,提供了用于注册并管理PropertyEditor的接口。

PropertyEditorRegistrySupport 是其默认实现,创建并注册了一些默认的PropertyEditor,并增加了对类型转换Convertion的支持。

图 PropertyEditorRegistrySupport UML

defaultEditors 及 customEditors 分别用来存储注册的默认及自定义editors。而overriddenDefaultEditors 是用来存储覆盖默认的editors。对应方法为overrideDefaultEditor。

public void overrideDefaultEditor(Class<?> requiredType, PropertyEditor propertyEditor) {if (this.overriddenDefaultEditors == null) {this.overriddenDefaultEditors = new HashMap<>();}this.overriddenDefaultEditors.put(requiredType, propertyEditor);
}

注册器的类型编辑器覆盖顺序为: 自定义editors -> ConversionService -> 覆盖默认editors -> 默认editors。 defaultEditors 最先被覆盖。boolean类型遍历configValueEditorsActive 用来控制在创建默认editors时,是否需要创建用于配置的editor(这类editor通常不适合用于数据绑定)。

图 createDefaultEditors 方法的部分截图

customEditorsForPath 是用来存储为特定属性路径注册的editor。

图 registerCustomEditor 方法

1.2 PropertyEditorRegistrar

PropertyEditor 注册器,用于把editors 注册到给定的registry中。

ResourceEditorRegistrar 是其默认实现。通常在Spring容器初始化时被调用。

图 ResourceEditorRegistrar UML

1.3 CustomEditorConfigurer

用于注册自定义Editor。可以在XML 或者使用注册来创建这个bean,并设置自定义editor属性。

图 CustomEditorConfigurer UML

<bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <!-- 设置 customEditors 属性 --> <property name="customEditors"> <map> <!-- 注册自定义的日期编辑器 --> <entry key="java.util.Date"> <bean class="org.springframework.beans.propertyeditors.CustomDateEditor"> <!-- 设置日期格式 --> <constructor-arg value="yyyy-MM-dd"/> <!-- 设置是否允许空值 --> <constructor-arg value="false"/> </bean> </entry> <!-- 可以添加更多的自定义编辑器 --> </map> </property> </bean> 

2 Conversion

可以替代PropertyEditor,主要用于在绑定数值时,将源类型转换为目标类型。

Spring 定义的Converter<S,T>接口,只定义了一个方法:

T convert(S source); // 将源类型转换为目标类型。

ConverterFactory<S,R> 接口,定义了一个工厂方法用于创建Converter实例:

<T extends R> Converter<S, T> getConverter(Class<T> targetType);

2.1 GenericConverter

支持在多个不同的源类型和目标类型之间进行转换。使用场景有:将不同类型的数值转换为集合,或者根据字段上的注解或泛型信息来驱动类型转换。

图 GenericConverter接口 UML

ConveriblePair 保存源类型及目标类型。

getConvertibleTypes 返回可以被转换的类型对(源类型与目标类型)。

ConditionalConverter 用于判断源类型是否可以转换的接口。

public interface ConditionalConverter {boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}

以下是Spring内部实现了GenericConverter及ConditionalConverter 的接口的ArrayToCollectionConverter(内部类,不对外部使用)源代码:

final class ArrayToCollectionConverter implements ConditionalGenericConverter {private final ConversionService conversionService;public ArrayToCollectionConverter(ConversionService conversionService) {this.conversionService = conversionService;}@Overridepublic Set<ConvertiblePair> getConvertibleTypes() {return Collections.singleton(new ConvertiblePair(Object[].class, Collection.class));}@Overridepublic boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {return ConversionUtils.canConvertElements(sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor(), this.conversionService);}@Override@Nullablepublic Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {if (source == null) {return null;}int length = Array.getLength(source);TypeDescriptor elementDesc = targetType.getElementTypeDescriptor();Collection<Object> target = CollectionFactory.createCollection(targetType.getType(),(elementDesc != null ? elementDesc.getType() : null), length);if (elementDesc == null) {for (int i = 0; i < length; i++) {Object sourceElement = Array.get(source, i);target.add(sourceElement);}}else {for (int i = 0; i < length; i++) {Object sourceElement = Array.get(source, i);Object targetElement = this.conversionService.convert(sourceElement,sourceType.elementTypeDescriptor(sourceElement), elementDesc);target.add(targetElement);}}return target;}}

2.2 ConversionService 与 ConverterRegistry

ConversionService定义了在运行期间执行转换的统一接口。

ConversionRegistry  Converter注册器,定义了用于添加/删除转换器的方法。

GenericConversionService 同时实现了这两个接口。而DefaultConversionService 继承了这个类,并增加了两个静态方法:

getSharedInstance(): 创建一个共享的DefaultConversionServices单例。

addDefaultConverters(ConverterRegistry converterRegistry):创建并注册一些默认的转换器。

2.3 ConversionServiceFactoryBean

用于添加自定义转换器的Bean。

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <!-- 在这里配置你的类型转换器 --> <property name="converters"> <list> <bean class="com.example.MyCustomConverter"/> <!-- 其他转换器 --> </list> </property> </bean>

其源代码如下:

public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {@Nullableprivate Set<?> converters;@Nullableprivate GenericConversionService conversionService;/*** Configure the set of custom converter objects that should be added:* implementing {@link org.springframework.core.convert.converter.Converter},* {@link org.springframework.core.convert.converter.ConverterFactory},* or {@link org.springframework.core.convert.converter.GenericConverter}.*/public void setConverters(Set<?> converters) {this.converters = converters;}@Overridepublic void afterPropertiesSet() {this.conversionService = createConversionService();ConversionServiceFactory.registerConverters(this.converters, this.conversionService);}/*** Create the ConversionService instance returned by this factory bean.* <p>Creates a simple {@link GenericConversionService} instance by default.* Subclasses may override to customize the ConversionService instance that* gets created.*/protected GenericConversionService createConversionService() {return new DefaultConversionService();}// implementing FactoryBean@Override@Nullablepublic ConversionService getObject() {return this.conversionService;}@Overridepublic Class<? extends ConversionService> getObjectType() {return GenericConversionService.class;}@Overridepublic boolean isSingleton() {return true;}}

这篇关于Spring 类型转换、数值绑定与验证(二)—PropertyEditor与Conversion的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/737720

相关文章

java如何解压zip压缩包

《java如何解压zip压缩包》:本文主要介绍java如何解压zip压缩包问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java解压zip压缩包实例代码结果如下总结java解压zip压缩包坐在旁边的小伙伴问我怎么用 java 将服务器上的压缩文件解压出来,

SpringBoot中SM2公钥加密、私钥解密的实现示例详解

《SpringBoot中SM2公钥加密、私钥解密的实现示例详解》本文介绍了如何在SpringBoot项目中实现SM2公钥加密和私钥解密的功能,通过使用Hutool库和BouncyCastle依赖,简化... 目录一、前言1、加密信息(示例)2、加密结果(示例)二、实现代码1、yml文件配置2、创建SM2工具

Spring WebFlux 与 WebClient 使用指南及最佳实践

《SpringWebFlux与WebClient使用指南及最佳实践》WebClient是SpringWebFlux模块提供的非阻塞、响应式HTTP客户端,基于ProjectReactor实现,... 目录Spring WebFlux 与 WebClient 使用指南1. WebClient 概述2. 核心依

Spring Boot @RestControllerAdvice全局异常处理最佳实践

《SpringBoot@RestControllerAdvice全局异常处理最佳实践》本文详解SpringBoot中通过@RestControllerAdvice实现全局异常处理,强调代码复用、统... 目录前言一、为什么要使用全局异常处理?二、核心注解解析1. @RestControllerAdvice2

Spring IoC 容器的使用详解(最新整理)

《SpringIoC容器的使用详解(最新整理)》文章介绍了Spring框架中的应用分层思想与IoC容器原理,通过分层解耦业务逻辑、数据访问等模块,IoC容器利用@Component注解管理Bean... 目录1. 应用分层2. IoC 的介绍3. IoC 容器的使用3.1. bean 的存储3.2. 方法注

Spring事务传播机制最佳实践

《Spring事务传播机制最佳实践》Spring的事务传播机制为我们提供了优雅的解决方案,本文将带您深入理解这一机制,掌握不同场景下的最佳实践,感兴趣的朋友一起看看吧... 目录1. 什么是事务传播行为2. Spring支持的七种事务传播行为2.1 REQUIRED(默认)2.2 SUPPORTS2

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

java中新生代和老生代的关系说明

《java中新生代和老生代的关系说明》:本文主要介绍java中新生代和老生代的关系说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、内存区域划分新生代老年代二、对象生命周期与晋升流程三、新生代与老年代的协作机制1. 跨代引用处理2. 动态年龄判定3. 空间分

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一