Skip to content

Converter接口

基本概念

在Spring框架中,Converter接口是一个简单的策略接口,用于将一个对象类型转换为另一个对象类型。它在Spring的类型转换系统中使用,可以在多种情况下自动应用,例如处理HTTP请求参数、处理表单提交的数据、设置bean属性等

注:仅针对@RequestParam注解(匹配默认为形参的对象名,例如PayTypeEnum payTypeEnumpayTypeEnumPayTypeEnum pp),无法对@RequestBody注解对象生效

Spring默认提供了一些内置的Converter,用于常见的数据类型转换。例如,Spring可以自动将字符串转换为基本数据类型(如intdoubleboolean等),以及一些常见的封装类型(如IntegerDoubleBoolean等)

如果需要自定义类型转换,可以通过实现Converter接口来创建自己的转换器,并将其注册到Spring的ConversionService中。当Spring在数据绑定过程中遇到需要转换的类型时,它会使用自定义的转换器。

使用场景

  • 处理HTTP请求参数:在Spring MVC中接收HTTP请求时,Spring会尝试将请求参数转换为控制器方法中声明的参数类型。如果需要将一个字符串参数转换为自定义对象,可以使用Converter
  • 设置Bean属性:在Spring的IoC容器中,当设置bean的属性时,如果类型不匹配,Spring会尝试使用注册的Converter进行类型转换
  • 数据绑定:使用DataBinder(非Web环境)或WebDataBinder(Web环境)用于将请求参数绑定到对象的属性上(数据绑定)。Converter可以用于自定义类型转换

源代码

Converter.java

java
package org.springframework.core.convert.converter;

import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

@FunctionalInterface
public interface Converter<S, T> {
    @Nullable
    T convert(S source);

    default <U> Converter<S, U> andThen(Converter<? super T, ? extends U> after) {
        Assert.notNull(after, "After Converter must not be null");
        return (s) -> {
            T initialResult = this.convert(s);
            return initialResult != null ? after.convert(initialResult) : null;
        };
    }
}
  • convert():需要重写的方法,定义转换器逻辑
  • andThen():提供默认的实现,可以通过使用此方式实现定义链式调用逻辑,最终通过返回的对象来使用convert()方法实现链式调用

示例代码

PayTypeEnum.java

java
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum PayTypeEnum {
    /**
     * 学生且周末
     */
    STUDENT_WEEKEND(1, "studentWeekendStrategy"),

    /**
     * 学生但工作日
     */
    student_working_day(2, "studentWorkdayStrategy"),

    /**
     * 非学生
     */
    not_student(3, "notStudentStrategy");

    /**
     * 支付类型Code
     */
    private final Integer payTypeCode;

    /**
     * 对应的策略(bean)名称
     */
    private final String strategyName;

    public static PayTypeEnum fromCode(int code) {
        for (PayTypeEnum payType : PayTypeEnum.values()) {
            if (payType.getPayTypeCode() == code) {
                return payType;
            }
        }
        throw new IllegalArgumentException("Invalid pay type code: " + code);
    }

    // 指定当前对象转换为JSON时所返回的值,可用于属性或者方法上
    @JsonValue
    public String getStrategyName() {
        return strategyName;
    }
}

StringToPayTypeEnumConverter.java

java
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

@Component
public class StringToPayTypeEnumConverter implements Converter<String, PayTypeEnum> {

    /**
     * 这里Converter<String, PayTypeEnum>
     *     第一个参数必须为String,第二个参数为自定义转换的对象类型
     * @param source 原始值
     * @return 注入的对象
     */
    @Override
    public PayTypeEnum convert(String source) {
        return PayTypeEnum.fromCode(Integer.parseInt(source));
    }
}

TestController.java

java
    @GetMapping("/useStrategy")
    public PayTypeEnum useStrategy(PayTypeEnum payTypeEnum) {
        testService.useStrategy(payTypeEnum);
        return payTypeEnum;
    }

当请求过来,SpringBoot会自动解析方法的请求体,当指定的对象与@RequestParam匹配成功,自动触发自定义的Converter转换器

http://localhost:8080/test/useStrategy?payTypeEnum=1

注:Spring MVC默认会将对象转化为键与请求参数赋值的,这里自定义转换逻辑之后就不会自动给对象中的属性赋值了

链式调用

StringToIntegerConverter.java

java
import org.springframework.core.convert.converter.Converter;

public class StringToIntegerConverter implements Converter<String, Integer> {

    @Override
    public Integer convert(String source) {
        System.out.println("将字符串数据转化为整型数据");
        return Integer.parseInt(source);
    }
}

IntegerToStringConverter.java

java
import org.springframework.core.convert.converter.Converter;

public class IntegerToStringConverter implements Converter<Integer, String> {

    @Override
    public String convert(Integer source) {
        System.out.println("将整型数据转化为字符串数据");
        return String.valueOf(source);
    }
}

StringToStringConverter.java

java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.converter.Converter;

public class StringToStringConverter implements Converter<String, String> {

    /**
     * 返回数据是Integer类型,相当于监听Integer类型
     */
    @Autowired
    private StringToIntegerConverter stringToIntegerConverter;

    /**
     * 由于SpringMVC为了适应处理,所以接受数据必须是字符串,这里接收不正确,无法触发
     */
    @Autowired
    private IntegerToStringConverter integerToStringConverter;

    @Override
    public String convert(String source) {
        // andThen实现链式调用,从左到右顺序执行
        Converter<String, String> resultConverter = stringToIntegerConverter.andThen(integerToStringConverter)
                .andThen(stringToIntegerConverter).andThen(integerToStringConverter);
        return resultConverter.convert(source);

        // 不能使用自身的andThen方法,会形成递归导致栈溢出
//        Converter<String, Integer> stringIntegerConverter = this.andThen(stringToIntegerConverter);
//        Integer convert = stringIntegerConverter.convert(source);
//        return String.valueOf(convert);
    }
}

ConverterConfig.java

java
import com.atguigu.boot.Converter.IntegerToStringConverter;
import com.atguigu.boot.Converter.StringToIntegerConverter;
import com.atguigu.boot.Converter.StringToStringConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;


@Configuration
public class ConverterConfig {

    @Bean
    public Converter<String, Integer> stringToIntegerConverter() {
        return new StringToIntegerConverter();
    }

    @Bean
    public Converter<Integer, String> integerToStringConverter() {
        return new IntegerToStringConverter();
    }

    @Bean
    public Converter<String, String> stringToStringConverter() {
        return new StringToStringConverter();
    }
}

TestController.java

java
    @RequestMapping("/use")
    public String test(String test) {
        System.out.println(test);
        return test;
    }

测试请求

localhost:8080/test/use?test=10

打印结果

将字符串数据转化为整型数据
将整型数据转化为字符串数据
将字符串数据转化为整型数据
将整型数据转化为字符串数据
10