文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-12-02 18:49

关注

环境:Springboot2.4.11

Spring3引入了一个core.convert包,它提供了一个通用类型转换系统。系统定义一个SPI来实现类型转换逻辑,定义一个API来在运行时执行类型转换。在Spring容器中,你可以使用此系统作为PropertyEditor实现的替代方案,将外部化的bean属性值字符串转换为所需的属性类型。你还可以在应用程序中需要进行类型转换的任何位置使用公共API。

Converter SPI

实现类型转换逻辑的SPI是简单且强类型的,如以下接口定义所示:

  1. package org.springframework.core.convert.converter; 
  2. public interface Converter { 
  3.   T convert(S source); 

要创建自己的转换器,需要实现converter接口,并将S参数化为要转换的类型,将T参数化为要转换的类型。如果需要将S的集合或数组转换为T的集合或集合,还可以透明地应用这样的转换器,前提是同时注册了委托数组或集合转换器(默认情况下,DefaultConversionService会这样做)。

对于每个转换调用,保证源参数source不为null。如果转换失败,转换器可能会抛出任何未检查的异常。具体来说,它应该抛出IllegalArgumentException以报告无效的源值。注意确保转换器实现是线程安全的。

为了方便起见,core.convert.support包中提供了几种转换器实现。其中包括从字符串到数字和其他常见类型的转换器。下表显示了StringToInteger类,它是典型的转换器实现:

  1. package org.springframework.core.convert.support; 
  2. final class StringToInteger implements ConverterInteger> { 
  3.   public Integer convert(String source) { 
  4.     return Integer.valueOf(source); 
  5.   } 

使用ConverterFactory

当需要集中整个类层次结构的转换逻辑时(例如,从字符串转换为枚举对象时),可以实现ConverterFactory,如下例所示:

  1. package org.springframework.core.convert.converter; 
  2. public interface ConverterFactory { 
  3.    Converter getConverter(Class targetType); 

将S参数化为要转换的类型,将R参数化为定义可转换为的类范围的基类型。然后实现getConverter(类),其中T是R的一个子类。

以StringToEnumConverterFactory为例:

  1. package org.springframework.core.convert.support; 
  2.  
  3. final class StringToEnumConverterFactory implements ConverterFactory { 
  4.  
  5.   public  Converter getConverter(Class targetType) { 
  6.     return new StringToEnumConverter(targetType); 
  7.   } 
  8.   private final class StringToEnumConverter implements Converter { 
  9.  
  10.     private Class enumType; 
  11.  
  12.     public StringToEnumConverter(Class enumType) { 
  13.       this.enumType = enumType; 
  14.     } 
  15.  
  16.     public T convert(String source) { 
  17.       return (T) Enum.valueOf(this.enumType, source.trim()); 
  18.     } 
  19.   } 

自定义类型转换

现在需要将接受的字符串转换为Users对象

  1. public class Users { 
  2.   private String name ; 
  3.   private Integer age ; 

接口

  1. @GetMapping("/convert2"
  2. public Object convert2(Users users) { 
  3.   return users ;  

调用接口


如上,通过get方式users的参数通过逗号分割。接下来就是写类型转换器了。

  1. @SuppressWarnings({"rawtypes""unchecked"}) 
  2. public class UsersConverterFactory implements ConverterFactory { 
  3.  
  4.   @Override 
  5.   public  Converter getConverter(Class targetType) { 
  6.     return new StringToUsersConverter() ; 
  7.   } 
  8.      
  9.   private final class StringToUsersConverter implements Converter { 
  10.     public Users convert(String source) { 
  11.       if (source == null || source.trim().length() == 0) { 
  12.         return null ; 
  13.       } 
  14.       Users user = new Users() ; 
  15.       // 下面做简单处理,不做校验 
  16.       String[] values = source.split(",") ; 
  17.       user.setName(values[0]) ; 
  18.       user.setAge(Integer.parseInt(values[1])); 
  19.       return user ; 
  20.     } 
  21.   } 

 注册类型转换器

  1. @Configuration 
  2. public class WebConfig implements WebMvcConfigurer { 
  3.  
  4.   @Override 
  5.   public void addFormatters(FormatterRegistry registry) { 
  6.     registry.addConverterFactory(new UsersConverterFactory()) ; 
  7.   } 
  8.      

编程方式使用类型转换器

要以编程方式使用ConversionService实例,可以像对任何其他bean一样向其注入引用。以下示例显示了如何执行此操作:

我们使用系统内置的类型转换器:字符串类型转枚举类型

  1. public enum PayStatus { 
  2.      
  3.   START, PROCESS, COMPLETE 
  4.      
  1. @RestController 
  2. @RequestMapping("/users"
  3. public class UsersController { 
  4.      
  5.   @Resource 
  6.   private ConversionService cs ; 
  7.    
  8.   @GetMapping("/convert"
  9.   public Object convert(String status) { 
  10.     boolean canConvert = cs.canConvert(String.class, PayStatus.class) ; 
  11.     return canConvert ? cs.convert(status, PayStatus.class) : "UNKNOW";  
  12.   } 
  13.    

先判断是否能够转换,其实就是判断有没有从source到target的类型转换器存在。

类型转换的实现原理

以自定义类型转换器为例

SpringMVC在进行接口调用是会执行相应的参数解析,确定了参数解析器后会执行转换服务。

查找参数解析器

查找合适的HandlerMethodArgumentResolver

  1. public class InvocableHandlerMethod extends HandlerMethod { 
  2.   protected Object[] getMethodArgumentValues(...) throws Exception {   
  3.     // 查找合适的参数解析器(本例应用的是ServletModelAttributeMethodProcessor) 
  4.     if (!this.resolvers.supportsParameter(parameter)) { 
  5.       throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver")); 
  6.     } 
  7.     try { 
  8.       args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); 
  9.     }       
  10.   } 

解析参数

执行

  1. public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver { 
  2.   public final Object resolveArgument(...) { 
  3.     attribute = createAttribute(name, parameter, binderFactory, webRequest);     
  4.   } 
  5. public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor { 
  6.   protected final Object createAttribute(String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception { 
  7.     // 这里得到的是原始值 
  8.     String value = getRequestValueForAttribute(attributeName, request); 
  9.     if (value != null) { 
  10.       Object attribute = createAttributeFromRequestValue(value, attributeName, parameter, binderFactory, request); 
  11.       if (attribute != null) { 
  12.         return attribute; 
  13.       } 
  14.     } 
  15.     return super.createAttribute(attributeName, parameter, binderFactory, request); 
  16.   }   
  17.   protected Object createAttributeFromRequestValue(String sourceValue, String attributeName,MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception { 
  18.     DataBinder binder = binderFactory.createBinder(request, null, attributeName); 
  19.     // ConversionService对象是在容器启动的时候就初始化好的 
  20.     // 在WebMvcAutoConfiguration#mvcConversionService方法中初始化。 
  21.     ConversionService conversionService = binder.getConversionService(); 
  22.     if (conversionService != null) { 
  23.       TypeDescriptor source = TypeDescriptor.valueOf(String.class); 
  24.       TypeDescriptor target = new TypeDescriptor(parameter); 
  25.       // 判断是否有合适的类型转换器 
  26.       if (conversionService.canConvert(source, target)) { 
  27.         // 此方法中进行类型的转换 
  28.         return binder.convertIfNecessary(sourceValue, parameter.getParameterType(), parameter); 
  29.       } 
  30.     } 
  31.     return null
  32.   } 

 

来源:今日头条内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯