当前位置: 首页 > 科技观察

Spring中自定义数据类型转换详解

时间:2023-03-14 00:53:00 科技观察

环境:Spring5.3.12.RELEASE。Spring3引入了一个提供通用类型转换系统的core.onvert包。系统定义了一个SPI来实现类型转换逻辑,以及一个API来在运行时进行类型转换。在Spring容器中,该系统可用作PropertyEditor实现的替代方案,以将外部化的bean属性值字符串转换为所需的属性类型。您还可以在应用程序中需要类型转换的任何地方使用公共API。1、类型转换服务ConversionService是类型转换服务的接口。publicinterfaceConversionService{//判断是否可以转换booleancanConvert(ClasssourceType,ClasstargetType);booleancanConvert(TypeDescriptorsourceType,TypeDescriptortargetType);//执行类型转换Tconvert(Objectsource,ClasstargetType);Objectconvert(Objectsource,TypeDescriptorsourceType,TypeDescriptortargetType);}在大多数情况下我们应该实现。ConfigurableConversionService可配置类型转换服务接口。该接口集成了ConversionService的所有操作和ConverterRegistry接口的相关操作,可以添加或删除特定的转换。后者在处理应用程序上下文引导代码中的ConfigurableEnvironment实例时特别有用。ConfigurableConversionService接口。publicinterfaceConfigurableConversionServiceextendsConversionService,ConverterRegistry{}Spring提供了一个GenericConversionService实现类;此类适用于大多数环境中使用的基本ConversionService实现。通过ConfigurableConversionService接口间接实现ConverterRegistry作为注册API。该类没有提供默认的类型转换功能,需要我们自己添加转换接口。示例:GenericConversionServicegcs=newGenericConversionService();Longresult=gcs.convert("10",Long.class);System.out.println(result);以上代码运行将报错:Exceptioninthread"main"org.springframework.core.convert.ConverterNotFoundException:在org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:322)在org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:195)在org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:175)在com.pack.main.conversion.GenericConversionServiceMain.main(GenericConversionServiceMain.java:9)没有转换接口出现错误。FormattingConversionService类是GenericConversionService的子类对象。它的主要功能是添加格式化功能(或者说是类型转换的一种表现形式)。该类提供对Printer和Parser的支持,可以打印和显示对象,并将源数据解析为目标对象。,例子:FormattingConversionServicefcs=newFormattingConversionService();fcs.addParser(newParser(){@OverridepublicTeacherparse(Stringtext,Localelocale)throwsParseException{String[]t=text.split("\\|");returnnewTeacher(t[0],Integer.valueOf(t[1]));}});System.out.println(fcs.convert("张晶晶|26",Teacher.class));这里的addParser方法最终将Parser转换为GenericConverter。将对象转换为可读信息,例子:FormattingConversionServicefcs=newFormattingConversionService();fcs.addPrinter(newPrinter(){@OverridepublicStringprint(Teacherobject,Localelocale){return"【name="+object.getName()+",age="+object.getAge()+"】";}});System.out.println(fcs.convert(newTeacher("张晶晶",26),String.class));上面介绍的类型转换服务默认没有任何类型转换能力,需要我们自己添加。Spring中也提供了DefaultConversionService和WebConversionService。看名字就知道WebConversionService是针对Web项目的,但您也可以在非Web项目中使用它。这里我就介绍一下DefaultConversionService。先看例子:DefaultConversionServicedcs=newDefaultConversionService();Longresult=dcs.convert("10",Long.class);Datedate=dcs.convert("2022-07-01",Date.class);系统。out.println(result);System.out.println(date);上面两种类型的转换都能成功,为什么?因为DefaultConversionService已经帮我们注册了很多类型转换,源码:publicclassDefaultConversionServiceextendsGenericConversionService{publicDefaultConversionService(){addDefaultConverters(this);}publicstaticvoidaddDefaultConverters(ConverterRegistryconverterRegistry){addScalarConverters(converterRegistry);//源码中也注册了很多集合和流数据类型转换函数addCollectionConverters(converterRegistry);converterRegistry.addConverter(newByteBufferConverter((ConversionService)converterRegistry));converterRegistry.addConverter(newStringToTimeZoneConverter());新的ZoneIdToTimeZoneConverter());转换器注册表。添加转换器(新的ZonedDateTimeToCalendarConverter());转换器寄存器istry.addConverter(newObjectToObjectConverter());converterRegistry.addConverter(新IdToEntityConverter((ConversionService)converterRegistry));converterRegistry.addConverter(newFallbackObjectToStringConverter());converterRegistry.addConverter(newObjectToOptionalConverter((ConversionService)converterRegistry));}}通常我们通常都使用DefaultConversionService在Web环境下默认使用的WebConversionService,这里以SpringBoot为例,源码如下:publicclassWebMvcAutoConfiguration{publicstaticclassEnableWebMvcConfigurationextendsDelegatingWebMvcConfigurationimplementsResourceLoaderAware{@Bean@OverridepublicFormattingConversionServicemvcConversionService(){Formatformat=this.mvcProperties.getFormat();WebConversionServiceconversionService=newWebConversionService(newDateTimeFormatters().dateFormat(format.getDate()).timeFormat(format.getTime()).dateTimeFormat(format.getDateTime()));添加格式化程序(转换服务);返回转换服务;}}}WebConversionService的继承关系publicclassWebConversionServiceextendsDefaultFormattingConversionService{}publicclassDefaultFormattingConversionServiceextendsFormattingConversionService{}publicclassFormattingConversionServiceextendsGenericConversionServiceimplementsFormatterRegistry,EmbeddedValueResolverAware{}Spring还提供了一个ConversionServiceFactoryBean来注册我们自定义的类型转换publicclassConversionServiceFactoryBeanimplementsFactoryBean,InitializingBean{privateSetconverters;私有GenericConversionService转换服务;publicvoidsetConverters(Setconverters){this.converters=converters;}@OverridepublicvoidafterPropertiesSet(){this.conversionService=createConversionService();ConversionServiceFactory.registerConverters(this.converters,this.conversionService);}protectedGenericConversionServicecreateConversionService(){returnnewDefaultConversionService();}@OverridepublicConversionServicegetObject(){returnthis.conversionService;}@Override公共类getObjectType(){returnGenericConversionService.class;}@OverridepublicbooleanisSingleton(){返回真;}}我们可以确定一个应该的Bean,然后注册转换器属性值。@BeanpublicConversionServiceFactoryBeanconversionService(){ConversionServiceFactoryBeanfactory=newConversionServiceFactoryBean();//自定义类型转换factory.setConverters(...);returnfactory;}Spring的类型转换服务是不是很简单?接下来介绍Spring提供的各种自定义类型转换方法。接下来我们将重点放在例子上。实现Converter接口Converter接口。@FunctionalInterfacepublicinterfaceConverter{Tconvert(Ssource);}自定义Converter接口,我们使用匿名内部类实现。DefaultConversionServicecs=newDefaultConversionService();//自定义类型转换器,有ConversionService时可以完全替代PropertyEditorcs.addConverter(newConverter(){publicUsersconvert(Stringsource){String[]temp=source.split("\\|");returnnewUsers(temp[0],Integer.parseInt(temp[1]));}});Usersusers=cs.convert("张三|100",Users.class);System.out.println(users);实现ConverterFactory接口当需要集中整个类层次结构的转换逻辑时(例如,从String对象转换为Enum对象时),可以实现ConverterFactory。即具有继承关系的类型转换。转换器工厂接口。publicinterfaceConverterFactory{ConvertergetConverter(ClasstargetType);}自定义工厂类。publicclassEnvObjectConvertimplementsConverterFactory{@OverridepublicConvertergetConverter(ClasstargetType){返回新的EnvConvert();}privateclassEnvConvertimplementsConverter{@OverridepublicTconvert(Stringsource){String[]temp=source.split("\\|");return(T)newEnvObject(temp[0],Integer.valueOf(temp[1]));}}}实现GenericConverter接口当您需要复杂的Converter实现时,请考虑使用GenericConverter接口。GenericConverter具有比Converter更灵活但类型更弱的签名,因此它支持多种源和目标类型之间的转换。此外,GenericConverter提供可用的源和目标字段上下文,您可以在实现转换逻辑时使用它们。这样的上下文允许类型转换通过字段注释或在字段签名上声明的通用信息来驱动。以下清单显示了GenericConverter的接口定义:GenericConverter接口。公共接口GenericConverter{SetgetConvertibleTypes();Objectconvert(@NullableObjectsource,TypeDescriptorsourceType,TypeDescriptortargetType);}自定义GenericConverter。publicclassCustomGenericConverterimplementsGenericConverter{@OverridepublicSetgetConvertibleTypes(){//这里可以定义多组类型转换关系ConvertiblePairteacherPair=newConvertiblePair(String.class,Teacher.class);ConvertiblePairstudentPair=newConvertiblePair(String.class,Student.class);Setpairs=newHashSet<>();pairs.add(teacherPair);pairs.add(studentPair);返回对;}@OverridepublicObjectconvert(Objectsource,TypeDescriptorsourceType,TypeDescriptortargetType){//看下面不同的类型处理不同Stringstr=null;if(sourceType.getObjectType()==String.class){str=(String)来源;}if(targetType.getObjectType()==Teacher.class){String[]t=str.split("\\|");returnnewTeacher(t[0],Integer.valueOf(t[1]));}if(targetType.getObjectType()==Student.class){String[]t=str.split("\\|");返回新学生(t[0],t[1]);}返回空值;}}