2018-12-11 · Develop

自定义类型转换器

Spring 内置了很多的类型转换器和转换服务。下面说说在做 RequestMapping 的数据绑定时比较常用的几种类型转换功能。

PropertyEditor

TypeConverter 提供了类型转换的接口,其默认的实现是 SimpleTypeConverter。 而 SimpleTypeConverter 继承至 PropertyEditorRegistrySupport 里面内置了许多的 PropertyEditor 属性编辑器。来个时间转换的栗子。

@GetMapping("/property_editor")
@ApiOperation("PropertyEditor 类型转换器")
public Date hello(Date date) {
    return date;
}

@InitBinder("date")
public void initDate(WebDataBinder binder) {
    binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
}

测试一下呢:

curl -X GET "http://localhost:8080/property_editor?date=2018-11-25"
"2018-11-24T16:00:00.000+0000"

Spring 早期版本都是通过 xml 配置的,而配置信息大多都是 String 类型,所以前期都是通过 PropertyEditor 就能满足大部分的类型转换场景,但是弊端也是很大,只能满足 String 和 Object 之间的转换。因此在 Spring3.x 引入了 Converter 和 Formatter 接口。

Formatter

Formatter 接口一样是实现的 String 与 Object 之间的转换,这个 PropertyEditor 有点类似,但是其比 PropertyEditor 更方便和轻量级。

@GetMapping("/formatter")
@ApiOperation("Formatter 类型转换器")
public Date hello(Date date) {
    return date;
}

在进行转换之前需要自己实现转换功能。

import org.springframework.format.Formatter;
import org.springframework.stereotype.Component;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

@Component
public class DateFormatter implements Formatter<Date> {

    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

    @Override
    public Date parse(String text, Locale locale) throws ParseException {
        return sdf.parse(text);
    }

    @Override
    public String print(Date object, Locale locale) {
        return sdf.format(object);
    }
}
curl -X GET "http://localhost:8080/formatter?date=2018-11-25"
"2018-11-24T16:00:00.000+0000"

如果是使用 xml 配置的话,需要进行添加到 FormattingConversionService 容器中, FormattingConversionService 是 spring-context 包提供的功能,其使用了策略模式,将具体的类型转换请求,交给对应的 GenericConverter 进行类型转换。FormattingConversionService 支持添加自定义的 Converter 和 Formatter,如果 Converter 不是 GenericConverter 接口的实现类,则会使用 ConverterAdapter 适配器将 Converter 转换成 GenericConverter 实现类;添加 Formatter 也是如此。

<bean id="formattingConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="formatters">
        <set>
            <bean class="com.example.swagger.common.DateFormatter"/>
        </set>
    </property>
</bean>

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

Converter

Converter 接口实现的是 Object <-> Object 之间的转换,功能最少强大。

@GetMapping("/converter")
@ApiOperation("Converter 类型转换器")
public Date hello(Date date) {
    return date;
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

@Slf4j
@Component
public class DateConverter implements Converter<String, Date> {

    @Nullable
    @Override
    public Date convert(String source) {
        try {
            return new SimpleDateFormat("yyyy-MM-dd").parse(source);
        } catch (ParseException e) {
            // e.printStackTrace();
            log.error("Parse String to Date on error.", e);
        }
        return null;
    }
}
curl -X GET "http://localhost:8080/converter?date=2018-11-25"
"2018-11-24T16:00:00.000+0000"

Formatter 一样,使用 xml 配置需要注册到容器中。

<bean id="formattingConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="com.example.swagger.common.DateConverter"/>
        </set>
    </property>
</bean>

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