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

Springboot强大的类型转换功能,必须掌握

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

环境:Springboot2.4.11Spring3引入了一个core.convert包,它提供了一个通用的类型转换系统。系统定义了一个SPI来实现类型转换逻辑,定义了一个API来在运行时进行类型转换。在Spring容器中,您可以使用此系统作为PropertyEditor实现的替代方案,将外部化的bean属性值字符串转换为所需的属性类型。您还可以在应用程序中需要类型转换的任何地方使用公共API。ConverterSPI实现类型转换逻辑的SPI简单且强类型,如下接口定义所示:packageorg.springframework.core.convert.converter;publicinterfaceConverter{Tconvert(Ssource);}创建自己的转换转换器需要实现转换器接口,将S参数化为要转换的类型,将T参数化为要转换的类型。如果S的集合或数组需要转换为T的集合或集合,也可以透明地应用这样的转换器,前提是委托数组或集合转换器也已注册(DefaultConversionService默认执行此操作)。对于每个转换调用,source参数source保证不为null。如果转换失败,转换器可能会抛出任何未经检查的异常。具体来说,它应该抛出一个IllegalArgumentException来报告无效的源值。注意确保转换器实现是线程安全的。为方便起见,core.convert.support包中提供了几个转换器实现。其中包括从字符串到数字和其他常见类型的转换器。下表显示了StringToInteger类,这是一个典型的转换器实现:packageorg.springframework.core.convert.support;finalclassStringToIntegerimplementsConverter{publicIntegerconvert(Stringsource){returnInteger.valueOf(source);}}需要时使用ConverterFactory当集中整个类层次结构的转换逻辑时(例如,当从字符串转换为枚举对象时),可以实现一个ConverterFactory,如下例所示:packageorg.springframework.core.convert.converter;publicinterfaceConverterFactory{ConvertergetConverter(ClasstargetType);}将S参数化为要转换的类型,将R参数化为定义它可以转换为的类范围的基类型。然后实现getConverter(class),其中T是R的子类。以StringToEnumConverterFactory为例:implementsConverter{privateClassenumType;publicStringToEnumConverter(ClassenumType){this.enumType=enumType;}publicTconvert(Stringsource){return(T)Enum.valueOf(this.enumType,source.trim());}}}自定义类型转换现在需要转换publicclassUsers{privateStringname;privateIntegerage;}interface@GetMapping("/convert2")publicObjectconvert2(Usersusers){returnusers;}调用接口如上,get方法中users的参数用逗号分隔。下一步是编写类型转换器。@SuppressWarnings({"rawtypes","unchecked"})publicclassUsersConverterFactoryimplementsConverterFactory{@OverridepublicConvertergetConverter(ClasstargetType){returnnewStringToUsersConverter();}privatefinalclassStringverToUsersConverter实现{publicUsersconvert(Stringsource){if(source==null||source.trim().length()==0){returnnull;}Usersuser=newUsers();//下面的简单处理,不是做验证String[]values=source.split(",");user.setName(values[0]);user.setAge(Integer.parseInt(values[1]));returnuser;}}}注册类型转换}}以编程方式使用类型转换器要以编程方式使用ConversionService实例,请像任何其他bean一样注入对它的引用。下面的例子展示了如何做到这一点:我们使用系统内置的类型转换器:字符串类型到枚举类型publicenumPayStatus{START,PROCESS,COMPLETE}@RestController@RequestMapping("/users")publicclassUsersController{@ResourceprivateConversionServicecs;@GetMapping("/convert")publicObjectconvert(Stringstatus){booleancanConvert=cs.canConvert(String.class,PayStatus.class);returncanConvert?cs.convert(status,PayStatus.class):"UNKNOW";}}先判断是否可以converted,其实就是判断从source到target是否有类型转换器。类型转换的实现原理以自定义类型转换器为例。SpringMVC在进行接口调用时会进行相应的参数解析,确定参数解析器后进行转换服务。找一个合适的参数解析器找一个合适的HandlerMethodArgumentResolverpublicclassInvocableHandlerMethodextendsHandlerMethod{protectedObject[]getMethodArgumentValues(...)throwsException{//找一个合适的参数解析器(本例中使用的是ServletModelAttributeMethodProcessor)if(!this.resolvers.supportsParameterExtendsStatenewIprowcellegll(parameter){(formatArgument(参数,“Nosuitableresolver”));}try{args[i]=this.resolvers.resolveArgument(参数,mavContainer,request,this.dataBinderFactory);}}}PublicclassModelAttributeMethodProcessorimplementsHandlerMethodArgumentResolver{publicfinalObjectresolveArgument(...{attribute=createAttribute(名称),parameter,binderFactory,webRequest);}}publicclassServletModelAttributeMethodProcessorextendsModelAttributeMethodProcessor{protectedfinalObjectcreateAttribute(StringattributeName,MethodParameterparameter,WebDataBinderFactorybinderFactory,NativeWebRequestrequest)throwsException{//这里得到的是原始值Stringvalue=getRequestValueForAttribute(attributeN)我,请求);if(value!=null){Objectattribute=createAttributeFromRequestValue(value,attributeName,parameter,binderFactory,request);if(attribute!=null){returnattribute;}}returnsuper.createAttribute(attributeName,parameter,binderFactory,request);}protectedObjectcreateAttributeFromRequestValue(StringsourceValue,StringattributeName,MethodParameterparameter,WebDataBinderFactorybinderFactory,NativeWebRequestrequest)throwsException{DataBinderbinder=binderFactory.createBinder(request,null,attributeName);//容器启动时初始化ConversionService对象#WebMConversioncAutom/InitializeConversionServiceconversionService=binder.getConversionService();if(conversionService!=null){TypeDescriptorsource=TypeDescriptor.valueOf(String.class);TypeDescriptortarget=newTypeDescriptor(parameter);//判断是否有合适的类型转换器if(conversionService.canConvert(source,target)){//在该方法中进行类型转换returnbinder.convertIfNecessary(sourceValue,parameter.getParameterType(),parameter);}}returnnull;}}