数据绑定与类型转换
SpringMVC提供强大的数据绑定和类型转换机制,自动将请求参数转换为目标类型。
DataBinder核心类
基本结构
Java
public class DataBinder {
private final Object target; // 目标对象
private final String objectName; // 对象名称
private BindingResult bindingResult; // 绑定结果
// 绑定属性值
public void bind(PropertyValues pvs) {
doBind(pvs);
}
// 执行绑定
protected void doBind(MutablePropertyValues mpvs) {
checkAllowedFields(mpvs);
checkRequiredFields(mpvs);
applyPropertyValues(mpvs);
}
}
WebDataBinder扩展
Java
public class WebDataBinder extends DataBinder {
// 从HttpServletRequest绑定参数
public void bind(HttpServletRequest request) {
MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
doBind(mpvs);
}
// 处理 multipart 请求
protected void bindMultipart(Map<String, List<MultipartFile>> multipartFiles,
MutablePropertyValues mpvs) {
for (Map.Entry<String, List<MultipartFile>> entry : multipartFiles.entrySet()) {
String key = entry.getKey();
List<MultipartFile> files = entry.getValue();
if (files.size() == 1) {
mpvs.add(key, files.get(0));
} else {
mpvs.add(key, files.toArray(new MultipartFile[0]));
}
}
}
}
ConversionService接口
核心接口
Java
public interface ConversionService {
boolean canConvert(Class<?> sourceType, Class<?> targetType);
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
<T> T convert(Object source, Class<T> targetType);
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
DefaultConversionService
Java
// Spring内置转换器
public class DefaultConversionService extends GenericConversionService {
public static void addDefaultConverters(ConverterRegistry registry) {
// 数值转换
addNumberConverters(registry);
// 字符串转换
addStringConverters(registry);
// 集合转换
addCollectionConverters(registry);
// 日期转换
addDateConverters(registry);
// 其他转换
addScalarConverters(registry);
}
}
Converter接口
核心接口
Java
public interface Converter<S, T> {
T convert(S source);
}
内置转换器示例
Java
// String -> Integer
final class StringToIntegerConverter implements Converter<String, Integer> {
@Override
public Integer convert(String source) {
return Integer.parseInt(source.trim());
}
}
// String -> Boolean
final class StringToBooleanConverter implements Converter<String, Boolean> {
@Override
public Boolean convert(String source) {
String value = source.trim().toLowerCase();
if ("true".equals(value) || "on".equals(value) || "yes".equals(value) || "1".equals(value)) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
}
自定义转换器
基本实现
Java
public class StringToDateConverter implements Converter<String, Date> {
private final String datePattern;
public StringToDateConverter(String datePattern) {
this.datePattern = datePattern;
}
@Override
public Date convert(String source) {
try {
SimpleDateFormat format = new SimpleDateFormat(datePattern);
return format.parse(source.trim());
} catch (ParseException e) {
throw new IllegalArgumentException("日期格式错误: " + source);
}
}
}
注册转换器
Java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
// 注册Converter
registry.addConverter(new StringToDateConverter("yyyy-MM-dd"));
registry.addConverter(new StringToEnumConverter());
// 注册Formatter(支持Locale)
registry.addFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss"));
}
}
ConditionalConverter
Java
// 条件转换器 - 根据条件决定是否转换
public interface ConditionalConverter {
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
// 示例:仅当目标类有特定注解时转换
public class AnnotationDrivenConverter
implements Converter<String, Object>, ConditionalConverter {
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return targetType.hasAnnotation(JsonFormat.class);
}
@Override
public Object convert(String source) {
// 自定义转换逻辑
return null;
}
}
ConverterFactory
Java
// 批量创建转换器(如所有枚举类型)
public interface ConverterFactory<S, R> {
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
// String到枚举的转换工厂
public class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
@Override
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnumConverter<>(targetType);
}
private static class StringToEnumConverter<T extends Enum>
implements Converter<String, T> {
private final Class<T> enumType;
public StringToEnumConverter(Class<T> enumType) {
this.enumType = enumType;
}
@Override
public T convert(String source) {
return (T) Enum.valueOf(this.enumType, source.trim().toUpperCase());
}
}
}
Formatter接口
Java
// Formatter支持Locale(国际化)
public interface Formatter<T> extends Printer<T>, Parser<T> {
String print(T object, Locale locale);
T parse(String text, Locale locale) throws ParseException;
}
// 日期格式化器
public class DateFormatter implements Formatter<Date> {
private String pattern;
public DateFormatter(String pattern) {
this.pattern = pattern;
}
@Override
public String print(Date date, Locale locale) {
return getDateFormat(locale).format(date);
}
@Override
public Date parse(String text, Locale locale) throws ParseException {
return getDateFormat(locale).parse(text);
}
protected DateFormat getDateFormat(Locale locale) {
SimpleDateFormat dateFormat = new SimpleDateFormat(pattern, locale);
dateFormat.setLenient(false);
return dateFormat;
}
}
数据绑定流程
ServletModelAttributeMethodProcessor
Java
public class ServletModelAttributeMethodProcessor
extends ModelAttributeMethodProcessor {
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) {
String name = ModelAttribute.getNameForParameter(parameter);
Object attribute = mavContainer.getAttribute(name);
if (attribute == null) {
// 创建目标对象
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
// 创建DataBinder并绑定
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
bindRequestParameters(binder, webRequest);
// 校验(如有@Valid)
validateIfApplicable(binder, parameter);
return binder.getTarget();
}
}
WebDataBinderFactory
Java
public class DefaultDataBinderFactory implements WebDataBinderFactory {
private final List<WebBindingInitializer> bindingInitializers;
public WebDataBinder createBinder(NativeWebRequest webRequest,
Object target, String objectName) {
WebDataBinder binder = createBinderInstance(target, objectName);
// 应用初始化器
if (this.bindingInitializers != null) {
for (WebBindingInitializer initializer : bindingInitializers) {
initializer.initBinder(binder, webRequest);
}
}
return binder;
}
}
类型转换流程图
Java
请求参数(String)
↓
HandlerMethodArgumentResolver
↓
WebDataBinder.bind()
↓
PropertyValues → applyPropertyValues()
↓
BeanWrapper.setPropertyValue()
↓
TypeConverterDelegate.convertIfNecessary()
↓
ConversionService.convert()
↓
Converter/Formatter实现类
↓
目标类型属性
WebBindingInitializer
Java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void initBinder(WebDataBinder binder, WebRequest request) {
// 设置允许的字段
binder.setAllowedFields("name", "age", "email");
// 设置必填字段
binder.setRequiredFields("name", "age");
// 设置字段标记
binder.setFieldMarkerPrefix("_");
// 注册自定义编辑器
binder.registerCustomEditor(Date.class,
new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
}
}
类型转换性能优化
text
// ConversionService缓存
public class GenericConversionService implements ConversionService {
private final Map<ConverterCacheKey, Converter<?, ?>> converterCache =
new ConcurrentHashMap<>(64);
public <T> T convert(Object source, Class<T> targetType) {
TypeDescriptor sourceType = TypeDescriptor.forObject(source);
TypeDescriptor targetDescriptor = TypeDescriptor.valueOf(targetType);
// 从缓存获取Converter
Converter<?, ?> converter = getConverter(sourceType, targetDescriptor);
return (T) converter.convert(source);
}
}
数据绑定通过BeanWrapper实现属性赋值,ConversionService负责类型转换,支持自定义扩展。
要点总结
- DataBinder负责将PropertyValues绑定到目标对象
- ConversionService统一管理类型转换
- Converter适用于单向转换,Formatter支持国际化格式化
- ConverterFactory批量创建同类型转换器
- WebBindingInitializer配置DataBinder初始参数
jwdev/articles/SPRINGMVC/专家/容器级WEB组件扩展/容器级WEB组件扩展/数据绑定与类型转换.md
📝 发现内容有误?点击此处直接编辑