SpringMVC中类型转换器Converter<S,T>详解

ConversionService是Spring类型转换体系的核心接口,可以利用conversionServiceFactoryBean在Spring工厂容器中定义一个conversionService

Spring将自动识别出ConversionService,并在bean属性配置及SpringMVC处理方法入参绑定等场合使用它进行数据转换
在这里插入图片描述

SpringMVC上下文中内建了很多转换器,可以完成大多数Java类型的转换工作。

【1】常见转换器接口

Spring定义了三种类型的转换器接口,实现任意一个接口都可以作为自定义转换器注册到ConversionServiceFactoryBean中。需要特别注意的是,这里的Convert接口与HttpMessageConverter接口截然不同,后者是用于从HTTP请求和响应转换为HTTP请求和响应的策略接口。

Converter<S,T>

:将S类型对象转换为T类型对象,该接口的实现是线程安全的、可以共享的。

@FunctionalInterface
public interface Converter<S, T> {
	//返回T对象
    @Nullable
    T convert(S var1);
}

默认内置了如下转换器

ObjectToStringConverter (org.springframework.core.convert.support)
LocalDateTimeToLocalTimeConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
StringToCharsetConverter (org.springframework.core.convert.support)
StringToPropertiesConverter (org.springframework.core.convert.support)
ConversionServiceConverter in ConvertingComparator (org.springframework.core.convert.converter)
CalendarToLocalDateTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
OffsetDateTimeToLocalTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
StringToCharacterConverter (org.springframework.core.convert.support)
ZonedDateTimeToLocalDateTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
ZonedDateTimeToInstantConverter in DateTimeConverters (org.springframework.format.datetime.standard)
LongToDateConverter in DateFormatterRegistrar (org.springframework.format.datetime)
NumberToNumber in NumberToNumberConverterFactory (org.springframework.core.convert.support)
ZonedDateTimeToLocalDateConverter in DateTimeConverters (org.springframework.format.datetime.standard)
DateToLongConverter in DateFormatterRegistrar (org.springframework.format.datetime)
StringToTimeZoneConverter (org.springframework.core.convert.support)
DateTimeToLocalDateConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
ZoneIdToTimeZoneConverter (org.springframework.core.convert.support)
OffsetDateTimeToInstantConverter in DateTimeConverters (org.springframework.format.datetime.standard)
LocalDateTimeToLocalDateConverter in DateTimeConverters (org.springframework.format.datetime.standard)
StringToBooleanConverter (org.springframework.core.convert.support)
InstantToLongConverter in DateTimeConverters (org.springframework.format.datetime.standard)
CalendarToLocalDateConverter in DateTimeConverters (org.springframework.format.datetime.standard)
PropertiesToStringConverter (org.springframework.core.convert.support)
DateTimeToDateMidnightConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
OffsetDateTimeToLocalDateTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
CalendarToReadableInstantConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
EnumToStringConverter (org.springframework.core.convert.support)
NumberToCharacterConverter (org.springframework.core.convert.support)
IntegerToEnum in IntegerToEnumConverterFactory (org.springframework.core.convert.support)
LocalDateTimeToLocalDateConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
StringToUUIDConverter (org.springframework.core.convert.support)
DateTimeToInstantConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
LongToReadableInstantConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
ZonedDateTimeToLocalTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
OffsetDateTimeToLocalDateConverter in DateTimeConverters (org.springframework.format.datetime.standard)
StringToCurrencyConverter (org.springframework.core.convert.support)
CalendarToInstantConverter in DateTimeConverters (org.springframework.format.datetime.standard)
StringToLocaleConverter (org.springframework.core.convert.support)
DateTimeToMutableDateTimeConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
DateTimeToDateConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
ZonedDateTimeToOffsetDateTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
StringToNumber in StringToNumberConverterFactory (org.springframework.core.convert.support)
DeserializingConverter (org.springframework.core.serializer.support)
LongToCalendarConverter in DateFormatterRegistrar (org.springframework.format.datetime)
DateToCalendarConverter in DateFormatterRegistrar (org.springframework.format.datetime)
DateTimeToLongConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
DateTimeToLocalTimeConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
LongToInstantConverter in DateTimeConverters (org.springframework.format.datetime.standard)
CharacterToNumber in CharacterToNumberFactory (org.springframework.core.convert.support)
CalendarToLongConverter in DateFormatterRegistrar (org.springframework.format.datetime)
EnumToIntegerConverter (org.springframework.core.convert.support)
ZonedDateTimeToCalendarConverter (org.springframework.core.convert.support)
OffsetDateTimeToZonedDateTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
CalendarToLocalTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
CalendarToOffsetDateTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
StringToEnum in StringToEnumConverterFactory (org.springframework.core.convert.support)
SerializingConverter (org.springframework.core.serializer.support)
DateTimeToLocalDateTimeConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
DateTimeToCalendarConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
CalendarToDateConverter in DateFormatterRegistrar (org.springframework.format.datetime)
DateToReadableInstantConverter in JodaTimeConverters (org.springframework.format.datetime.joda)
CalendarToZonedDateTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)
LocalDateTimeToLocalTimeConverter in DateTimeConverters (org.springframework.format.datetime.standard)

② ConverterFactory

将相同系列多个"同质" Converter封装在一起。如果希望将一种类型的对象转换为另一种类型及其子类的对象(例如将String转换为Number及Number的子类(Integer、Long、Double等)对象)。

注解源码如下

public interface ConverterFactory<S, R> {
	 //获取转换器,该转换器可以将S转换为目标T类型(T  extends R)
	<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}

其是典型的工厂模式,子类具体工厂(都是final不可继承)负责生产对应的Convert:

  • CharacterToNumberFactory:static final class CharacterToNumber,Character–Number;
  • StringToEnumConverterFactory :static class StringToEnum,String–Enum;
  • IntegerToEnumConverterFactory:static class IntegerToEnum,Integer–Enum;
  • NumberToNumberConverterFactory:static final class NumberToNumber,Number–Number;
  • StringToNumberConverterFactory:static final class StringToNumber,String–Number

final修饰类时表示该类不允许被继承,final类中的成员方法都会被隐式的指定为final方法。
static修饰类时只能修饰内部类,此时内部类可以直接创建实例、可以有静态成员变量、方法(普通内部类不行)和非静态成员变量、方法。

③ GenericConverter

用于在两个或多个类型之间转换的通用转换器接口。这是最灵活的转换器SPI接口,也是最复杂的。

它的灵活性在于,GenericConverter 可能支持在多个源/目标类型对之间进行转换。此外,GenericConverter 实现在类型转换过程中可以访问源/目标。这允许解析可用于影响转换逻辑的源和目标字段元数据,如注解和泛型信息。

当更简单的 Converter或ConverterFactory接口足够时,通常不应使用此接口。

会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换。

public interface GenericConverter {

	 //返回此转换器可以转换的源类型和目标类型。每一个实体都是一个可以转换的 source-to-target类型对
	@Nullable
	Set<ConvertiblePair> getConvertibleTypes();

	 //将源对象转换为TypeDescriptor 描述的类型,返回转换后的对象
	@Nullable
	Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);


	/**
	 * Holder for a source-to-target class pair.
	 */
	final class ConvertiblePair {

		private final Class<?> sourceType;

		private final Class<?> targetType;
	//...
	}
}

【2】自定义转换器

如果想把一个字符串转换成其它实体类型,spring没有提供这样默认的功能,我们需要自定义类型转换器。

① 自定义类型转换器

需求如下:这里有个实体类Employee,将传输参数(String类型)转换为Employee。

//实现了Converter<String, Employee> 接口
@Component
public class EmployeeConverter implements Converter<String, Employee> {

	@Override
	public Employee convert(String source) {
		if(source != null){
			String [] vals = source.split("-");
			//GG-gg@web.com-0-105
			if(vals != null && vals.length == 4){
				String lastName = vals[0];
				String email = vals[1];
				Integer gender = Integer.parseInt(vals[2]);
				Department department = new Department();
				department.setId(Integer.parseInt(vals[3]));
				
				Employee employee = new Employee(null, lastName, email, gender, department);
				System.out.println(source + "--convert--" + employee);
				return employee;
			}
		}
		return null;
	}

}

② 注册到容器

xml方式注册

可通过ConversionServiceFactoryBeanconverters属性注册自定义的类型转换器。示例如下:

<!-- 配置 ConversionService 数据类型的转换和格式化 -->
	<bean id="conversionService"
		class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
		<property name="converters">
			<set>
				<ref bean="employeeConverter"/>
				<!-- 自定义转换器  以前的一样存在 -->
			</set>
		</property>	
	</bean>

<mvc:annotation-driven conversion-service="conversionService"/>
<!--将自定义的conversionService注册到Spring MVC的上下文中-->

上面配置文件中我们使用配置了一个FormattingConversionServiceFactoryBean。虽然可以使用ConversionServiceFactoryBean,但是不推荐。

SpringBoot下使用java config注册

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
   		 registry.addConverter(new EmployeeConverter());
        // ...
    }
}

【3】ConverterFactory使用实例

实践背景:根据code(字符串)转换为一个枚举对象。

① MyEnum

MyEnum实现了接口BaseEnum 。

public enum  MyEnum implements BaseEnum {
    enum1(1,"enum1"), enum2(2,"enum2");

    private int code;
    private String name;

    MyEnum(int code, String name) {
        this.code = code;
        this.name = name;
    }

    @Override
    public String toString() {
        return String.valueOf(code);
    }

    @Override
    public int code() {
        return code;
    }
}

② MyConver

MyConver将String转换为BaseEnum的子类。

@Component
public class MyConver<T extends BaseEnum> implements Converter<String, T> {
    @Override
    public T convert(String source) {
        switch (source){
            case "1":return (T)MyEnum.enum1;
            case "2":return (T)MyEnum.enum2;
        }
        return (T)MyEnum.enum1;
    }
}

③ MyConvertFactory

MyConvertFactory 的核心思想就是根据targetType获取对应的Converter。

@Component
public class MyConvertFactory implements ConverterFactory<String,BaseEnum> {
    @Override
    public <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) {
        if(targetType ==BaseEnum.class){
            return new MyConver<>();
        }
        return new MyConver<>();
    }
}

然后注册到Spring中:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
   		 registry.addConverterFactory(new MyConvertFactory());
        // ...
    }
}

实际开发中,常常会遇到“数据格式化”的场景。那么如何格式化数据?其实就是通过使用转换器将数据转换为目标格式。


【4】FormattingConversionService

Spring 在格式化模块中定义了一个实现ConversionService接口的FormattingConversionService实现类。该实现类扩展了GenericConversionService,因此它既具有类型转换的功能又具有格式化的功能。

FormattingConversionService拥有一个FormattingConversionServiceFactoryBean工厂类,后者用于在Spring上下文中构造前者。
在这里插入图片描述
FormattingConversionService内部注册了两个工厂类:

NumberFormatAnnotationFormatterFactory:支持对数字类型的属性使用@NumberFormat注解;

JodaDateTimeFormatAnnotationFormatterFactory:支持对日期类型的属性使用@DateTimeFormat注解。

这下就明白了,为什么可以使用@NumberFormat和@DateTimeFormat注解进行数据格式化了

装配了FormattingConversionServiceFactoryBean后,就可以在SpringMVC入参绑定及模型数据输出时使用注解驱动了。

FormattingConversionServiceFactoryBeanafterPropertiesSet方法很有意思,在bean实例化后会获取DefaultFormattingConversionService实例,然后注册类型转换器和格式化器Formatter

public void afterPropertiesSet() {
		this.conversionService = new DefaultFormattingConversionService(this.embeddedValueResolver, this.registerDefaultFormatters);
		ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
		registerFormatters(this.conversionService);
	}

SpringMVC配置文件示例

<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>	

<!-- 配置 ConversionService 数据类型的转换和格式化 -->
<bean id="conversionService"
	class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
	<!--注意这里是FormattingConversionServiceFactoryBean-->
	<property name="converters">
		<set>
			<ref bean="employeeConverter"/>
			<!-- 自定义转换器  以前的一样存在 -->
		</set>
	</property>	
</bean>

【5】日期格式化和数值格式化

示例代码如下图所示:

这里写图片描述


下面分别简要介绍一下上述两个注解

① @DateTimeFormat注解

该注解可对java.util.Date , java.util.Calendar , java.long.Long 时间类型进行标注。

① pattern属性:

类型为字符串。指定解析/格式化字段数据的模式,如“yyyy-MM-dd HH:mm:ss”。

注意:如果是12小时制,使用 hh:mm:ss ; 如果是24小时制,使用HH:mm:ss。


② iso属性

类型为DateTimeFormat.ISO。指定解析/格式化字段数据的ISO模式,包括四种:

ISO.NONE;
ISO.DATE(yyyy-MM-dd);
ISO.TIME(hh:mm:ss.SSSZ);
ISO.DATE_TIME(yyyy-MM-dd hh:mm:ss.SSSZ)。

③ style属性

字符串类型。通过样式指定日期时间的格式,由两位字符组成,第一位表示日期的格式,第二位表示时间的格式。

  • S:短日期/时间格式;
  • M:中日期/时间格式;
  • L:长日期/时间格式;
  • F:完整日期/时间格式;
  • -:忽略日期或时间格式。

② @NumberFormat注解

该注解可对类似数字类型的属性进行标注,它有两个互斥的属性:

① style

类型为NumberFormat.Style。用于指定样式类型,包括三种:

Style.NUMBER(正常数字类型);
Style.CURRENCY(货币类型);
Style.PERCENT(百分数类型)。

② pattern

类型为String,自定义样式,如pattern="#,###"

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

流烟默

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

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

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

打赏作者

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

抵扣说明:

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

余额充值