SpringBoot - Web类型转换器
1. 类型转换器概述
类型转换器就是将一个类型转换为另一个类型,Spring提供的类型转换器多达上百种,
比如可以将String类型转换为数字类型。
同时Spring也提供的类型转换器的接口Converter
:
// org.springframework.core.convert.converter.Converter
@FunctionalInterface
public interface Converter<S, T> {
/**
* Convert the source object of type {@code S} to target type {@code T}.
* @param source the source object to convert, which must be an instance of {@code S} (never {@code null})
* @return the converted object, which must be an instance of {@code T} (potentially {@code null})
* @throws IllegalArgumentException if the source cannot be converted to the desired target type
*/
@Nullable
T convert(S source);
/**
* Construct a composed {@link Converter} that first applies this {@link Converter}
* to its input, and then applies the {@code after} {@link Converter} to the
* result.
* @param after the {@link Converter} to apply after this {@link Converter}
* is applied
* @param <U> the type of output of both the {@code after} {@link Converter}
* and the composed {@link Converter}
* @return a composed {@link Converter} that first applies this {@link Converter}
* and then applies the {@code after} {@link Converter}
* @since 5.3
*/
default <U> Converter<S, U> andThen(Converter<? super T, ? extends U> after) {
Assert.notNull(after, "After Converter must not be null");
return (S s) -> {
T initialResult = convert(s);
return (initialResult != null ? after.convert(initialResult) : null);
};
}
}
我们可以通过实现convert
方法来根据需求编写自定义类型转换器。
2. SpringBoot定制化类型转换器
自定义类型转换器毫无疑问要通过SpringBoot定制化实现,通过上节的学习,
我们知道可以通过自定义WebMvcConfigurer
的方法实现SpringMVC的定制化。
我们可以重写WebMvcConfigurer
的addFormatters
方法来添加自定义类型转换器:
// org.springframework.web.servlet.config.annotation.WebMvcConfigurer.addFormatters
// WebMvcConfigurer.java Line:86~87
/**
* Add {@link Converter Converters} and {@link Formatter Formatters} in addition to the ones
* registered by default.
*
* 添加类型转换器(Converter)和格式化器(Formatter)
* 使用registry的addConverter方法添加类型转换器
* 使用registry的addFormatter方法添加格式化器
*/
default void addFormatters(FormatterRegistry registry) {
}
3. 编写自定义类型转换器
我们可以通过编写自定义转换器类和使用lambda表达式的方式来编写自定义类型转换器。
3.1 编写自定义类型转换器类
- 自定义转换器类(实现Converter接口并重写convert方法):
public class StringToUserConverter implements Converter<String, User> {
@Override
public User convert(String source) {
// TODO 在此处编写转换逻辑
}
}
- 在配置类中创建自定义WebMvcConfigurer并重写addFormatters方法导入我们编写的类型转换器类:
@Configuration
public class SpringConfig {
@Bean
public WebMvcConfigurer webMvcConfig() {
return new WebMvcConfigurer() {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToUserConverter());
}
};
}
}
3.2 使用匿名内部类或lambda表达式
匿名内部类:
@Configuration
public class SpringConfig {
@Bean
public WebMvcConfigurer webMvcConfig() {
return new WebMvcConfigurer() {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new Converter<String, User>() {
@Override
public User convert(String source) {
// TODO 此处编写转换逻辑
}
});
}
};
}
}
lambda表达式:
@Configuration
public class SpringConfig {
@Bean
public WebMvcConfigurer webMvcConfig() {
return new WebMvcConfigurer() {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter((Converter<String, User>) source -> {
// TODO 在此编写转换逻辑
});
}
};
}
}
4. 类型转换器原理&自定义POJO类型数据绑定原理
4.1 数据绑定原理(调试过程)
继续上节的学习,跟踪调试ModelAttributeMethodProcessor的resolveArgument方法:
// org.springframework.web.method.annotation.ModelAttributeMethodProcessor
// ModelAttributeMethodProcessor.java Line:119~186
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");
// 获取参数的名称(如果有@ModelAttribute注解的话使用注解内的名称,如果没有的话再去调用其他方法获取参数名)
String name = ModelFactory.getNameForParameter(parameter);
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
// 如果当前参数使用了@ModelAttribute注解,则直接绑定隐含模型中的对应数据
if (ann != null) {
mavContainer.setBinding(name, ann.binding())