Spring类型转换器相关接口和实现原理

源码见:mini-spring

在这里插入图片描述

Spring 的 类型转换(Type Conversion) 是指:在应用中将一种 Java 类型的对象转换成另一种类型对象的机制。

在实际开发中,我们经常会遇到“类型不匹配”问题,比如:

  • 表单提交的字符串要转换成数字、日期、枚举等;

  • 配置文件中的字符串要转换成 Boolean、List、Class;

  • Bean 属性注入时类型不一致;

  • SpEL 表达式中的值需要自动转换类型;

  • 数据绑定(如 Web MVC 参数绑定)中,request 参数是字符串,要绑定到业务对象中。

Spring 提供了统一的类型转换框架来处理这些场景。

本节就Spring如何实现类型转换展开叙述,下节介绍如何将其融入生命周期当中

一、核心组件

1. ConversionService(核心接口)

Spring 类型转换的核心服务接口,负责执行类型之间的转换:

public interface ConversionService {
    boolean canConvert(Class<?> sourceType, Class<?> targetType);
    <T> T convert(Object source, Class<T> targetType);
}

  • 可以检查是否支持某种转换。
  • 可以执行实际的转换。

2. Converter<S, T>

这是最基本的类型转换器接口,Spring 中的所有类型转换器都实现了该接口:

public interface ConversionService {
    boolean canConvert(Class<?> sourceType, Class<?> targetType);
    <T> T convert(Object source, Class<T> targetType);
}

  • S:源类型
  • T:目标类型

例如,将字符串转为整数的转换器:

public class StringToIntegerConverter implements Converter<String, Integer> {
    public Integer convert(String source) {
        return Integer.valueOf(source);
    }
}


3. GenericConverter

更灵活但也更复杂的转换器,适合处理多个源/目标类型的场景:

public class StringToIntegerConverter implements Converter<String, Integer> {
    public Integer convert(String source) {
        return Integer.valueOf(source);
    }
}

  • 支持多个源类型与目标类型组合。

  • 使用 TypeDescriptor 解决泛型、注解等更复杂的类型问题。


4. ConverterFactory<S, R>

适用于一组相关类型转换的情况。例如:String 转为 Enum 的所有子类型:

public interface ConverterFactory<S, R> {
    <T extends R> Converter<S, T> getConverter(Class<T> targetType);
}

二、底层实现

在上一节中,我们简要了解了类型转换的核心组件。实际上,Spring 提供了三种不同类型的转换器接口:

  • 普通类型转换器(Converter
  • 类型转换工厂(ConverterFactory
  • 通用类型转换器(GenericConverter

下面将逐一进行讲解。

1,普通类型转换器 Converter<S, T>

public interface Converter<S,T> {  
    T convert(S source);  
}

该接口结构非常简洁,只定义了一个核心方法 convert,用于将源类型 S 转换为目标类型 T。使用时只需实现此接口并提供具体的转换逻辑即可。

例如,将 String 转换为 Integer

public class StringToIntegerConverter implements Converter<String,Integer> {  
    @Override  
    public Integer convert(String source) {  
        return Integer.valueOf(source);  
    }  
}

测试一下

@Test  
public void testStringToIntegerConverter(){  
    StringToIntegerConverter converter = new StringToIntegerConverter();  
    Integer integer = converter.convert("10");  
    Assert.assertEquals(Integer.valueOf(10),integer);  
}

2,类型转换工厂 ConverterFactory<S, R>

相较于 Converter 接口,ConverterFactory 适用于更广泛的转换需求。它可以通过工厂方法,为某一源类型 S 生成多个不同目标子类型 R 的转换器,常用于统一处理一组目标类型。

public interface ConverterFactory<S, R> {  
  
    /**  
     * 根据目标类型获取转换器对象  
     *  
     * @param <T> 目标类型参数,表示具体的转换后数据类型,必须是R类型或其子类型  
     * @param targetType 目标类型的Class对象,用于指定转换后的数据类型  
     * @return 返回一个Converter对象,用于将源数据类型S转换为目标类型T  
     */    
     <T extends R> Converter<S,T> getConverter(Class<T> targetType);  
}

以下是一个将 String 转换为多个数字类型(如 IntegerLong)的工厂实现:

public class StringToNumberConverterFactory implements ConverterFactory<String,Number> {  
    /**  
     * 根据目标类型获取转换器对象  
     *  
     * @param targetType 目标类型的Class对象,用于指定转换后的数据类型  
     * @return 返回一个Converter对象,用于将源数据类型S转换为目标类型T  
     */ 
       
    @Override  
    public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {  
        return new StringToNumber<T>(targetType);  
    }  
  
  
    public static final class StringToNumber<T extends Number> implements  Converter<String,T>{  
  
        private final Class<T> targetType;  
  
        public StringToNumber(Class<T> targetType) {  
            this.targetType = targetType;  
        }  
  
        @Override  
        public T convert(String source) {  
            if (source.length() == 0){  
                return  null;  
            }  
  
            if (targetType.equals(Integer.class)){  
                return (T) Integer.valueOf(source);  
            } else if (targetType.equals(Long.class)) {  
                return (T) Long.valueOf(source);  
            }else {  
                throw new IllegalArgumentException(  
                        "Cannot convert String [" + source + "] to target class [" + targetType.getName() + "]");  
            }  
        }  
    }  
}

测试一下

@Test  
public void testStringToNumberConverterFactory(){  
    StringToNumberConverterFactory converterFactory = new StringToNumberConverterFactory();  
    Converter<String, Integer> converter = converterFactory.getConverter(Integer.class);  
    Integer integer = converter.convert("10");  
    Assert.assertEquals(Integer.valueOf(10),integer);  
}

3,通用类型转换器 GenericConverter

GenericConverter 是 Spring 提供的最灵活、功能最强大的转换接口,适用于更复杂的场景,包括:

  • 支持多个源类型和目标类型的转换(如 String → List<Integer>String → Set<String>);
  • 利用运行时类型信息(通过 TypeDescriptor)进行精确判断;
  • 适配泛型、注解、字段等更复杂的上下文信息。
public interface GenericConverter {  
  
    // 执行类型转换逻辑  
    Object convert(Object source, Class sourceType, Class targetType);  
  
    // 获取到对应的ConvertiblePair配对关系对象  
    Set<ConvertiblePair> getConvertibleTypes();  
  
  
    /**  
     * 用于管理“源类型”和“目标类型”的配对关系  
     */  
    public static final class ConvertiblePair{  
        private final Class<?> sourceType;  
        private final Class<?> targetType;  
  
  
        public ConvertiblePair(Class<?> sourceType, Class<?> targetType) {  
            this.sourceType = sourceType;  
            this.targetType = targetType;  
        }  
  
        public Class<?> getSourceType() {  
            return sourceType;  
        }  
  
        public Class<?> getTargetType() {  
            return targetType;  
        }  
  
        @Override  
        public boolean equals(Object object) {  
            if (object == null || getClass() != object.getClass()) return false;  
            ConvertiblePair that = (ConvertiblePair) object;  
            return Objects.equals(sourceType, that.sourceType) && Objects.equals(targetType, that.targetType);  
        }  
  
        @Override  
        public int hashCode() {  
            return Objects.hash(sourceType, targetType);  
        }  
    }  
}

我们可以看到在GenericConverter我们还定义了一个静态的内部类ConvertiblePair,该类是用来用于管理“源类型”和“目标类型”的配对关系。

简单来说,我们来思考一个问题当我们调用GenericConverter的convert去尝试转换类型,而GenericConverter是一个接口,具体的各种转换逻辑由子类实现,那我们又如何才能判断出,我要转换的类型是否合法,当前类型转换器当中是否支持?

所以这里就引入了ConvertiblePair用来表示一个“源类型 → 目标类型”的一组转换类型

测试一下

@Test  
public void testGenericConverter(){  
    StringToBooleanConverter converter = new StringToBooleanConverter();  
    Boolean aTrue =(Boolean)  converter.convert("true", String.class, Boolean.class);  
    Assert.assertTrue(aTrue);  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

攒了一袋星辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值