Spring通用类型转换的实现原理

Spring通用类型转换的实现原理

    • 设计思路
    • 实现逻辑
      • ConversionService:类型转换服务入口
      • ConverterRegister:转换器注册接口
      • GenericConversionService
        • 1. `Map<ConvertiblePair, GenericConverter> converters`
        • 2. `canConvert()` 与 `convert()`:服务入口
        • 3. `getConverter()`:匹配最合适的转换器
        • 4. `addConverter()` 和 `addConverterFactory()`
        • 5. `getRequiredTypeInfo()`
        • 6. 完整代码

源码见:mini-spring

在这里插入图片描述

设计思路

在前面我们聊了Spring类型转换的三种实现方式,但现在也遇到一个问题,在后续我们将类型转换融入Spring生命周期的时候对于这三种方式如何选择,如何管理。就从这个角度来看的话,是不是还不如只定义一种方式。

对于这个问题Spring也给出了解决方案:

在类型转换系统中保留一个核心注册接口 + 多种适配实现,是 Spring 推荐的设计;但对于业务开发者或自定义扩展场景,通常只需选择一种实现方式(推荐 Converter<S, T>)并注册到统一的转换服务中即可。真正集成到 Spring 生命周期中,核心是统一注册到 ConversionService 中。

可以用以下任意方式注册:

GenericConversionService conversionService = new GenericConversionService();  
conversionService.addConverter(new StringToIntegerConverter());  
Integer integer = conversionService.convert("10", Integer.class);  
Assert.assertEquals(Integer.valueOf(10),integer);  conversionService.addConverterFactory(new StringToNumberConverterFactory());  
Long aLong = conversionService.convert("10", Long.class);  
Assert.assertEquals(Long.valueOf(10),aLong);

统一管理入口:通过 ConversionService 统一进行类型转换调用 ,简单来说就是通过适配器模式将Converter与ConverterFactory封装为GenericConverter。

实现逻辑

ConversionService:类型转换服务入口

public interface ConversionService {  /**  * 检查是否可以将源类型转换为目标类型  *  * @param sourceType 源类型的Class对象  * @param targetType 目标类型的Class对象  * @return 如果可以进行转换则返回true,否则返回false  */    boolean canConvert(Class<?> sourceType, Class<?> targetType);  /**  * 将给定的源对象转换为目标类型  *  * @param source 要转换的源对象  * @param targetType 目标类型的Class对象  * @param <T> 目标类型的泛型参数  * @return 转换后的目标类型对象  */  <T> T convert(Object source, Class<T> targetType);  
}
  • 判断是否支持某种类型对的转换

  • 提供统一的类型转换执行入口

ConverterRegister:转换器注册接口

public interface ConverterRegister {  void addConverter(Converter<?, ?> converter);  void addConverter(GenericConverter genericConverter);  void addConverterFactory(ConverterFactory converterFactory);  
}
  • 三种类型转换器的注册入口

  • 最终都会适配为 GenericConverter 存储

GenericConversionService

Spring 支持三种转换器接口(ConverterConverterFactoryGenericConverter),但使用上不应让开发者关心这些差异。

目的:

  • 将三种接口适配为统一的 GenericConverter 类型

  • 统一注册到一个容器(这里是 Map<ConvertiblePair, GenericConverter>

  • 提供 canConvert()convert() 方法对外暴露转换能力

1. Map<ConvertiblePair, GenericConverter> converters
  • 核心转换器容器:key 是 “源类型 → 目标类型” 的类型对,value 是对应的转换器。
Map<ConvertiblePair, GenericConverter> converters = new HashMap<>();

2. canConvert()convert():服务入口
@Override
public boolean canConvert(Class<?> sourceType, Class<?> targetType)

判断是否存在某种类型转换器(从 sourceType → targetType

@Override
public <T> T convert(Object source, Class<T> targetType)

实际执行转换,内部通过 getConverter() 获取匹配的 GenericConverter


3. getConverter():匹配最合适的转换器
protected GenericConverter getConverter(Class<?> sourceType, Class<?> targetType)

核心逻辑:

  • 递归获取 source 和 target 的继承结构(getClassHierarchy()

  • 生成 ConvertiblePair 对象

  • converters 中查找是否存在该对的转换器

支持子类转父类、接口匹配等灵活匹配机制


4. addConverter()addConverterFactory()
public void addConverter(Converter<?, ?> converter)
  • 将实现了 Converter<S, T> 的对象适配为 GenericConverter(通过 ConverterAdapter

  • 注册到 converters 容器中

public void addConverterFactory(ConverterFactory<?, ?> converterFactory)
  • 同理,适配 ConverterFactoryGenericConverter(通过 ConverterFactoryAdapter

5. getRequiredTypeInfo()
private ConvertiblePair getRequiredTypeInfo(Object object)

这个方法利用 反射 + 泛型解析

  • 读取类所实现的接口的泛型参数类型

  • 自动识别该转换器是支持 S → T 哪两个类型

举例:

class StringToIntegerConverter implements Converter<String, Integer>

会被解析为:

ConvertiblePair(String.class, Integer.class)
6. 完整代码

public class GenericConversionService implements ConversionService, ConverterRegister {  // 用于存储通用转换器  private Map<ConvertiblePair,GenericConverter> converters = new HashMap();  /**  * 检查是否可以将源类型转换为目标类型  *  * @param sourceType 源类型的Class对象  * @param targetType 目标类型的Class对象  * @return 如果可以进行转换则返回true,否则返回false  */    @Override  public boolean canConvert(Class<?> sourceType, Class<?> targetType) {  GenericConverter converter = getConverter(sourceType, targetType);  return converter != null;  }  /**  * 将给定的源对象转换为目标类型  *  * @param source     要转换的源对象  * @param targetType 目标类型的Class对象  * @return 转换后的目标类型对象  */  @Override  public <T> T convert(Object source, Class<T> targetType) {  Class<?> sourceType = source.getClass();  GenericConverter converter = getConverter(sourceType, targetType);  return (T) converter.convert(source, sourceType, targetType);  }  /**  * 获取通用适配器  * @param sourceType  * @param targetType     * @return     */    protected GenericConverter getConverter(Class<?> sourceType, Class<?> targetType){  List<Class<?>> sourceHierarchy = getClassHierarchy(sourceType);  List<Class<?>> targetHierarchy = getClassHierarchy(targetType);  for (Class<?> sourceCandidate : sourceHierarchy) {  for (Class<?> targetCandidate : targetHierarchy) {  ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);  GenericConverter genericConverter = converters.get(convertiblePair);  if (genericConverter != null){  return genericConverter;  }  }  }  return null;  }  // 获取目标类的完整继承层级结构,以便于通过ConvertiblePair判断转换器是否已注册  protected List<Class<?>> getClassHierarchy(Class<?> clazz){  List<Class<?>>  hierarchy = new ArrayList<>();  while ((clazz != null)){  hierarchy.add(clazz);  clazz = clazz.getSuperclass();  }  return hierarchy;  }  @Override  public void addConverter(Converter<?, ?> converter) {  // 这里传入的是一个Converter转换器对象,我们需要将其转化为GenericConverter对象  ConvertiblePair requiredTypeInfo = getRequiredTypeInfo(converter);  ConverterAdapter converterAdapter = new ConverterAdapter(converter, requiredTypeInfo);  for (ConvertiblePair convertibleType : converterAdapter.getConvertibleTypes()) {  converters.put(convertibleType,converterAdapter);  }  }  @Override  public void addConverterFactory(ConverterFactory converterFactory) {  ConvertiblePair requiredTypeInfo = getRequiredTypeInfo(converterFactory);  ConverterFactoryAdapter converterFactoryAdapter = new ConverterFactoryAdapter(converterFactory, requiredTypeInfo);  for (ConvertiblePair convertibleType : converterFactoryAdapter.getConvertibleTypes()) {  converters.put(convertibleType,converterFactoryAdapter);  }  }  /**  * 获取到目标类所实现的泛型接口的元素  * @param object  * @return     */    private ConvertiblePair getRequiredTypeInfo(Object object){  Type[] types = object.getClass().getGenericInterfaces();  ParameterizedType parameterized = (ParameterizedType) types[0];  Type[] actualTypeArguments = parameterized.getActualTypeArguments();  Class sourceType = (Class) actualTypeArguments[0];  Class targetType = (Class) actualTypeArguments[1];  return new ConvertiblePair(sourceType, targetType);  }  // 通过适配器模式对Converter以及ConverterFactory进行适配  private final class ConverterAdapter implements GenericConverter{  private final Converter converter;  private final ConvertiblePair convertiblePair;  private ConverterAdapter(Converter converter, ConvertiblePair convertiblePair) {  this.converter = converter;  this.convertiblePair = convertiblePair;  }  @Override  public Object convert(Object source, Class sourceType, Class targetType) {  return converter.convert(source);  }  @Override  public Set<ConvertiblePair> getConvertibleTypes() {  return Collections.singleton(convertiblePair);  }  }  private final class ConverterFactoryAdapter implements GenericConverter{  private final ConverterFactory converterFactory;  private final ConvertiblePair convertiblePair;  private ConverterFactoryAdapter(ConverterFactory converterFactory, ConvertiblePair convertiblePair) {  this.converterFactory = converterFactory;  this.convertiblePair = convertiblePair;  }  @Override  public Object convert(Object source, Class sourceType, Class targetType) {  return converterFactory.getConverter(targetType).convert(source);  }  @Override  public Set<ConvertiblePair> getConvertibleTypes() {  return Collections.singleton(convertiblePair);  }  }  
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/news/908656.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

红黑树完全指南:为何工程都用它?原理、实现、场景、误区全解析

红黑树完全指南&#xff1a;为何工程都用它&#xff1f;原理、实现、场景、误区全解析 作者&#xff1a;星之辰 标签&#xff1a;#红黑树 #平衡二叉查找树 #工程实践 #数据结构 #面试宝典 引子&#xff1a;工程师的“性能焦虑”与树的进化史 你以为树只是算法题里的配角&#…

阿里云 RDS mysql 5.7 怎么 添加白名单 并链接数据库

阿里云 RDS mysql 5.7 怎么 添加白名单 并链接数据库 最近帮朋友 完成一些运维工作 &#xff0c;这里记录一下。 文章目录 阿里云 RDS mysql 5.7 怎么 添加白名单 并链接数据库最近帮朋友 完成一些运维工作 &#xff0c;这里记录一下。 阿里云 RDS MySQL 5.7 添加白名单1. 登录…

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题&#xff1a; 指定音频引擎与设备&#xff1b;播放音频文件 本文所使用的环境&#xff1a; Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…

分布式互斥算法

1. 概述&#xff1a;什么是分布式互斥 假设有两个小孩想玩同一个玩具&#xff08;临界资源&#xff09;&#xff0c;但玩具只有一个&#xff0c;必须保证一次只有一个人能够玩。当一个小孩在玩时&#xff0c;另一个小孩只能原地等待&#xff0c;直到玩完才能轮到自己。这就是 …

[创业之路-410]:经济学 - 国富论的核心思想和观点,以及对创业者的启发

一、国富论的核心思想和观点 《国富论》全称为《国民财富的性质和原因的研究》&#xff0c;由英国经济学家亚当斯密于1776年出版&#xff0c;是经济学领域的经典之作&#xff0c;其核心思想和观点对现代经济学的发展产生了深远影响&#xff0c;具体如下&#xff1a; 劳动价值…

Tavily 技术详解:为大模型提供实时搜索增强的利器

目录 &#x1f680; Tavily 技术详解&#xff1a;为大模型提供实时搜索增强的利器 &#x1f9e9; 为什么需要 Tavily&#xff1f; &#x1f50d; Tavily 是什么&#xff1f; 核心特性&#xff1a; &#x1f4e6; Tavily 在 RAG 架构中的位置 &#x1f9ea; 示例&#xff…

欣佰特科技亮相2025张江具身智能开发者大会:呈现人形机器人全链条解决方案

5月29日 &#xff0c;2025年张江具身智能开发者大会在上海落下帷幕。欣佰特科技作为专注人形机器人与具身智能领域的创新企业&#xff0c;携一系列前沿产品与解决方案参展&#xff0c;与全球行业专家、企业共同探讨技术落地路径&#xff0c;展现其在具身智能领域的技术积累与场…

@Prometheus 监控-MySQL (Mysqld Exporter)

文章目录 **Prometheus 监控 MySQL ****1. 目标****2. 环境准备****2.1 所需组件****2.2 权限要求** **3. 部署 mysqld_exporter****3.1 下载与安装****3.2 创建配置文件****3.3 创建 Systemd 服务****3.4 验证 Exporter** **4. 配置 Prometheus****4.1 添加 Job 到 prometheus…

MCP Resource模块详解

MCP Resource模块详解 摘要 MCP Resource模块是模型上下文协议的核心组件&#xff0c;通过标准化URI接口为AI模型提供安全可控的只读数据访问能力。其核心设计包括数据隔离架构和客户端驱动的访问控制&#xff0c;支持文本/二进制编码格式&#xff0c;适用于配置文件读取、数据…

Docker 容器化基础:镜像、容器与仓库的本质解析

Docker 概念与容器化技术 Docker 是一种容器化平台&#xff0c;能够将应用程序及其依赖项打包成一个容器&#xff0c;确保在任何环境中都能一致运行。容器化技术通过操作系统级别的虚拟化&#xff0c;为应用程序提供了一个独立的运行环境。 容器化技术的核心优势 一致性&…

解决SQL Server SQL语句性能问题(9)——SQL语句改写(2)

9.4.3. update语句改写 与Oracle类似,SQL Server中,update语句被用户相关技术人员广泛应用于现实日常工作中。但是,有些情况下,尤其是海量数据场景中,update语句也许会带来性能方面的严重问题或极大隐患。因此,为了解决和消除update语句导致的性能问题或隐患,我们将需对…

Unity VR/MR开发-VR/开发SDK选型对比分析

视频讲解链接&#xff1a; 【XR马斯维】Unity开发VR/MR用哪些SDK&#xff1f;【UnityVR/MR开发教程--入门】_哔哩哔哩_bilibili

Python 高效图像帧提取与视频编码:实战指南

Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…

java复习 05

我的天啊一天又要过去了&#xff0c;没事的还有时间&#xff01;&#xff01;&#xff01; 不要焦虑不要焦虑&#xff0c;事实证明只要我认真地投入进去一切都还是来得及的&#xff0c;代码多实操多复盘&#xff0c;别叽叽喳喳胡思乱想多多思考&#xff0c;有迷茫前害怕后的功…

《Go小技巧易错点100例》第三十五篇

本期分享&#xff1a; 1.循环依赖导致栈溢出 2.无法捕获子协程的panic 循环依赖导致栈溢出 在Go语言开发中&#xff0c;我们经常会遇到结构体之间需要相互引用的情况。当两个结构体直接或间接地相互包含对方作为自己的字段时&#xff0c;就会形成循环依赖。 但是在Go语言中…

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…

.NET 9中的异常处理性能提升分析:为什么过去慢,未来快

一、为什么要关注.NET异常处理的性能 随着现代云原生、高并发、分布式场景的大量普及&#xff0c;异常处理&#xff08;Exception Handling&#xff09;早已不再只是一个冷僻的代码路径。在高复杂度的微服务、网络服务、异步编程环境下&#xff0c;服务依赖的外部资源往往不可…

第二十九章 数组

第二十九章 数组 数组。所有编程语言中都少不了数组,Shell语言也不例外,只不过支持程度非常有限。即便如此,在解决某些编程问题时,数组也能发挥大作用。 什么是数组 数组是一种可以一次存放多个值的变量,其组织形式类似与表格。数组中的每个变量叫做元素,每个元素都含…

ffmpeg(五):裁剪与合并命令

裁剪&#xff08;剪切&#xff09; 精准裁剪&#xff08;有转码&#xff0c;支持任意起止时间&#xff09; # 从第 10 秒到第 30 秒&#xff0c;重新编码 ffmpeg -i input.mp4 -ss 00:00:10 -to 00:00:30 -c:v libx264 -c:a aac output.mp4快速裁剪&#xff08;无转码&#x…

20、typedef和typename

在C中&#xff0c;typedef和typename有不同的用途和语法。以下是它们的主要区别&#xff1a; typedef typedef用于为现有类型定义一个新的名字。它通常用于简化复杂类型声明&#xff0c;使代码更易读。 示例&#xff1a; typedef unsigned long ulong; typedef int (*func_…