SpringMvc 参数解析的过程中需要进行数据类型的转换,如String变int,date变Long……,在类型转换的过程中还需要对数据的格式进行校验。
Java中的属性编辑器
PropertyEditor
PropertyEditor是属性编辑器的接口,它规定了将外部设置值转换为内部JavaBean属性值的转换接口方法。
public interface PropertyEditor {
//设置属性的值,基本类型以包装类传入(自动装箱)
void setValue(Object value);
//返回属性的当前值。基本类型被封装成对应的包装类实例
Object getValue();
boolean isPaintable();
void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box);
String getJavaInitializationString();
//----------------------------------------------------------------------
//将属性对象用一个字符串表示,以便外部的属性编辑器能以可视化的方式显示。缺省返回null,表示该属性不能以字符串表示。
String getAsText();
//用给定的字符串设置属性值,如果格式不正确或者属性不是text则抛出异常
void setAsText(String text) throws java.lang.IllegalArgumentException;
//----------------------------------------------------------------------
String[] getTags();
//获取自定义属性编辑器
java.awt.Component getCustomEditor();
//是否支持自定义编辑器
boolean supportsCustomEditor();
void addPropertyChangeListener(PropertyChangeListener listener);
void removePropertyChangeListener(PropertyChangeListener listener);
}
PropertyEditorSupport
Java为PropertyEditor提供了一个方便的实现类:PropertyEditorSupport,该类实现了PropertyEditor接口并提供默认实现。
public String getAsText() {
return (this.value != null)
? this.value.toString()
: null;
}
public void setAsText(String text) throws java.lang.IllegalArgumentException {
if (value instanceof String) {
setValue(text);
return;
}
throw new java.lang.IllegalArgumentException(text);
}
PropertyEditorSupport的实现只能是简单的String<——>Object之间转换,不能任意对象<——>任意类型之间的转换。Spring自己实现了各种类型的属性编辑器。
Spring的属性编辑器
Spring中有很多自定义的属性编辑器,都在spring-beans jar包下的
org.springframework.beans.propertyeditors包里。这些属性编辑器都继承自PropertyEditorSupport。
- 基本数据类型,如:boolean、byte、short、int等;
- 基本数据类型封装类,如:Long、Character、Integer等;
- 两个基本数据类型的数组,char[]和byte[];
- 时间类型 date、timezone
- 集合类Collection、Set、SortedSet、List和SortedMap
- 访问外部资源的常见类Class、Class[]、File、InputStream、Locale、Properties、Resource[]和URL
Spring数据类型转换
上面的属性编辑器中我们知道了Spring实现了各个类型的属性编辑器,因此Spring就有能力可以把各个种类的数据进行转换,下面我们看一下Spring是如何实现数据转换的。
属性编辑器注册
PropertyEditorRegistry接口
public interface PropertyEditorRegistry {
void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);
void registerCustomEditor(Class<?> requiredType, String propertyPath, PropertyEditor propertyEditor);
PropertyEditor findCustomEditor(Class<?> requiredType, String propertyPath);
}
PropertyEditorRegistrySupport——PropertyEditorRegistry接口的基础实现类
private ConversionService conversionService;
private boolean defaultEditorsActive = false;
private boolean configValueEditorsActive = false;
//默认属性编辑器集合
private Map<Class<?>, PropertyEditor> defaultEditors;
//重写的默认属性编辑器集合
private Map<Class<?>, PropertyEditor> overriddenDefaultEditors;
//自定义的属性编辑器集合
private Map<Class<?>, PropertyEditor> customEditors;
//自定义路径属性编辑器集合
private Map<String, CustomEditorHolder> customEditorsForPath;
//自定义属性编辑器缓存
private Map<Class<?>, PropertyEditor> customEditorCache;
createDefaultEditors 添加默认属性编辑器
private void createDefaultEditors() {
this.defaultEditors = new HashMap<Class<?>, PropertyEditor>(64);
// Simple editors, without parameterization capabilities.
// The JDK does not contain a default editor for any of these target types.
this.defaultEditors.put(Charset.class, new CharsetEditor());
this.defaultEditors.put(Class.class, new ClassEditor());
this.defaultEditors.put(Class[].class, new ClassArrayEditor());
this.defaultEditors.put(Currency.class, new CurrencyEditor());
this.defaultEditors.put(File.class, new FileEditor());
this.defaultEditors.put(InputStream.class, new InputStreamEditor());
this.defaultEditors.put(InputSource.class, new InputSourceEditor());
this.defaultEditors.put(Locale.class, new LocaleEditor());
if (pathClass != null) {
this.defaultEditors.put(pathClass, new PathEditor());
}
this.defaultEditors.put(Pattern.class, new PatternEditor());
this.defaultEditors.put(Properties.class, new PropertiesEditor());
this.defaultEditors.put(Reader.class, new ReaderEditor());
this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
this.defaultEditors.put(URI.class, new URIEditor());
this.defaultEditors.put(URL.class, new URLEditor());
this.defaultEditors.put(UUID.class, new UUIDEditor());
if (zoneIdClass != null) {
this.defaultEditors.put(zoneIdClass, new ZoneIdEditor());
}
// Default instances of collection editors.
// Can be overridden by registering custom instances of those as custom editors.
this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));
// Default editors for primitive arrays.
this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());
// The JDK does not contain a default editor for char!
this.defaultEditors.put(char.class, new CharacterEditor(false));
this.defaultEditors.put(Character.class, new CharacterEditor(true));
// Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));
// The JDK does not contain default editors for number wrapper types!
// Override JDK primitive number editors with our own CustomNumberEditor.
this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));
// Only register config value editors if explicitly requested.
if (this.configValueEditorsActive) {
StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
this.defaultEditors.put(String[].class, sae);
this.defaultEditors.put(short[].class, sae);
this.defaultEditors.put(int[].class, sae);
this.defaultEditors.put(long[].class, sae);
}
}
类型转换
TypeConverter接口
public interface TypeConverter {
<T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException;
<T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam)
throws TypeMismatchException;
<T> T convertIfNecessary(Object value, Class<T> requiredType, Field field)
throws TypeMismatchException;
}
TypeConverterSupport:TypeConverter基础实现类,并继承了PropertyEditorRegistrySupport
public abstract class TypeConverterSupport extends PropertyEditorRegistrySupport implements TypeConverter {
//类型转换委托类。具体的类型转换操作由此类完成。
TypeConverterDelegate typeConverterDelegate;
@Override
public <T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException {
return doConvert(value, requiredType, null, null);
}
@Override
public <T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam)
throws TypeMismatchException {
return doConvert(value, requiredType, methodParam, null);
}
@Override
public <T> T convertIfNecessary(Object value, Class<T> requiredType, Field field)
throws TypeMismatchException {
return doConvert(value, requiredType, null, field);
}
private <T> T doConvert(Object value, Class<T> requiredType, MethodParameter methodParam, Field field)
throws TypeMismatchException {
try {
if (field != null) {
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, field);
}
else {
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
}
}
catch (ConverterNotFoundException ex) {
throw new ConversionNotSupportedException(value, requiredType, ex);
}
catch (ConversionException ex) {
throw new TypeMismatchException(value, requiredType, ex);
}
catch (IllegalStateException ex) {
throw new ConversionNotSupportedException(value, requiredType, ex);
}
catch (IllegalArgumentException ex) {
throw new TypeMismatchException(value, requiredType, ex);
}
}
}
TypeConverterDelegate
数据的转换在TypeConverterDelegate里实现,下面看部分代码
class TypeConverterDelegate {
//属性编辑器注册中心,持有各种Spring自定义的属性编辑器
private final PropertyEditorRegistrySupport propertyEditorRegistry;
private final Object targetObject;
public <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue,
Class<T> requiredType, TypeDescriptor typeDescriptor) throws IllegalArgumentException {
// 根据类型找到自定义的属性编辑器
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException conversionAttemptEx = null;
// 没有自定义的属性编辑器但是有指定的conversionService
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
conversionAttemptEx = ex;
}
}
}
Object convertedValue = newValue;
// 值不是需要的类型
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
//需要collection类型
if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
convertedValue instanceof String) {
TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
if (elementTypeDesc != null) {
Class<?> elementType = elementTypeDesc.getType();
if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
}
}
//没有自定义的属性编辑器,则使用默认的属性编辑器
if (editor == null) {
editor = findDefaultEditor(requiredType);
}
//根据给定的属性编辑器将数据类型转换
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}
boolean standardConversion = false;
if (requiredType != null) {
// 根据需要,按类型的标准进行转换
if (convertedValue != null) {
//需要转换成Object类型,结果直接返回
if (Object.class == requiredType) {
return (T) convertedValue;
}
//需要Array类型,进行相应转换
else if (requiredType.isArray()) {
// Array required -> apply appropriate conversion of elements.
if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
}
//转换成相应的collection类型
else if (convertedValue instanceof Collection) {
// Convert elements to target type, if determined.
convertedValue = convertToTypedCollection(
(Collection<?>) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
//转换成相应的map类型
else if (convertedValue instanceof Map) {
// Convert keys and values to respective target type, if determined.
convertedValue = convertToTypedMap(
(Map<?, ?>) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
convertedValue = Array.get(convertedValue, 0);
standardConversion = true;
}
if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
// We can stringify any primitive value...
return (T) convertedValue.toString();
}
else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
if (conversionAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) {
try {
Constructor<T> strCtor = requiredType.getConstructor(String.class);
return BeanUtils.instantiateClass(strCtor, convertedValue);
}
catch (NoSuchMethodException ex) {
// proceed with field lookup
if (logger.isTraceEnabled()) {
logger.trace("No String constructor found on type [" + requiredType.getName() + "]", ex);
}
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Construction via String failed for type [" + requiredType.getName() + "]", ex);
}
}
}
String trimmedValue = ((String) convertedValue).trim();
if (requiredType.isEnum() && "".equals(trimmedValue)) {
// It's an empty enum identifier: reset the enum value to null.
return null;
}
convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
standardConversion = true;
}
//number类型的数据转换成需要的int、short等类型
else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) {
convertedValue = NumberUtils.convertNumberToTargetClass(
(Number) convertedValue, (Class<Number>) requiredType);
standardConversion = true;
}
}
else {
// convertedValue == null
if (javaUtilOptionalEmpty != null && requiredType == javaUtilOptionalEmpty.getClass()) {
convertedValue = javaUtilOptionalEmpty;
}
}
if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
if (conversionAttemptEx != null) {
// Original exception from former ConversionService call above...
throw conversionAttemptEx;
}
else if (conversionService != null) {
// ConversionService not tried before, probably custom editor found
// but editor couldn't produce the required type...
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
}
// Definitely doesn't match: throw IllegalArgumentException/IllegalStateException
StringBuilder msg = new StringBuilder();
msg.append("Cannot convert value of type '").append(ClassUtils.getDescriptiveType(newValue));
msg.append("' to required type '").append(ClassUtils.getQualifiedName(requiredType)).append("'");
if (propertyName != null) {
msg.append(" for property '").append(propertyName).append("'");
}
if (editor != null) {
msg.append(": PropertyEditor [").append(editor.getClass().getName()).append(
"] returned inappropriate value of type '").append(
ClassUtils.getDescriptiveType(convertedValue)).append("'");
throw new IllegalArgumentException(msg.toString());
}
else {
msg.append(": no matching editors or conversion strategy found");
throw new IllegalStateException(msg.toString());
}
}
}
if (conversionAttemptEx != null) {
if (editor == null && !standardConversion && requiredType != null && Object.class != requiredType) {
throw conversionAttemptEx;
}
logger.debug("Original ConversionService attempt failed - ignored since " +
"PropertyEditor based conversion eventually succeeded", conversionAttemptEx);
}
return (T) convertedValue;
}
}
JavaBean类型转换
PropertyAccessor接口
对类中属性操作的接口。
public interface PropertyAccessor {
String NESTED_PROPERTY_SEPARATOR = ".";
char NESTED_PROPERTY_SEPARATOR_CHAR = '.';
String PROPERTY_KEY_PREFIX = "[";
char PROPERTY_KEY_PREFIX_CHAR = '[';
String PROPERTY_KEY_SUFFIX = "]";
char PROPERTY_KEY_SUFFIX_CHAR = ']';
boolean isReadableProperty(String propertyName);
boolean isWritableProperty(String propertyName);
Class<?> getPropertyType(String propertyName) throws BeansException;
TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException;
Object getPropertyValue(String propertyName) throws BeansException;
void setPropertyValue(String propertyName, Object value) throws BeansException;
void setPropertyValue(PropertyValue pv) throws BeansException;
void setPropertyValues(Map<?, ?> map) throws BeansException;
void setPropertyValues(PropertyValues pvs) throws BeansException;
void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown)
throws BeansException;
void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
throws BeansException;
}
BeanWrapper接口
Spring底层JavaBeans基础结构的核心接口。继承ConfigurablePropertyAccessor(ConfigurablePropertyAccessor继承PropertyAccessor、PropertyEditorRegistry、TypeConverter接口)。
public interface BeanWrapper extends ConfigurablePropertyAccessor {
//省略方法
}
public interface ConfigurablePropertyAccessor extends PropertyAccessor, PropertyEditorRegistry, TypeConverter {
//省略方法
}
BeanWrapperImpl类
BeanWrapper接口的默认实现类,TypeConverterSupport是它的父类,可以进行类型转换,可以进行属性设置。
convertForProperty
给定的值按照特定的属性进行转换。
public Object convertForProperty(Object value, String propertyName) throws TypeMismatchException {
CachedIntrospectionResults cachedIntrospectionResults = getCachedIntrospectionResults();
PropertyDescriptor pd = cachedIntrospectionResults.getPropertyDescriptor(propertyName);
if (pd == null) {
throw new InvalidPropertyException(getRootClass(), getNestedPath() + propertyName,
"No property '" + propertyName + "' found");
}
TypeDescriptor td = cachedIntrospectionResults.getTypeDescriptor(pd);
if (td == null) {
td = cachedIntrospectionResults.addTypeDescriptor(pd, new TypeDescriptor(property(pd)));
}
return convertForProperty(propertyName, null, value, td);
}
AbstractNestablePropertyAccessor中的转换方法
具体的转换工作是由TypeConverterDelegate来完成的。
public abstract class TypeConverterSupport extends PropertyEditorRegistrySupport implements TypeConverter {
TypeConverterDelegate typeConverterDelegate;
//省略不少代码
protected Object convertForProperty(String propertyName, Object oldValue, Object newValue, TypeDescriptor td)
throws TypeMismatchException {
return convertIfNecessary(propertyName, oldValue, newValue, td.getType(), td);
}
private Object convertIfNecessary(String propertyName, Object oldValue, Object newValue, Class<?> requiredType,
TypeDescriptor td) throws TypeMismatchException {
try {
return this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td);
}
catch (ConverterNotFoundException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue);
throw new ConversionNotSupportedException(pce, td.getType(), ex);
}
catch (ConversionException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue);
throw new TypeMismatchException(pce, requiredType, ex);
}
catch (IllegalStateException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue);
throw new ConversionNotSupportedException(pce, requiredType, ex);
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue);
throw new TypeMismatchException(pce, requiredType, ex);
}
}
}
数据绑定
DataBinder
实现PropertyEditorRegistry、TypeConverter的类。支持类型转换,参数验证,数据绑定等功能。
public class DataBinder implements PropertyEditorRegistry, TypeConverter {
//绑定数据的目标对象
private final Object target;
private final String objectName;
//
private AbstractPropertyBindingResult bindingResult;
//绑定器SimpleTypeConverter
private SimpleTypeConverter typeConverter;
}
参数绑定
protected void doBind(MutablePropertyValues mpvs) {
checkAllowedFields(mpvs);
checkRequiredFields(mpvs);
applyPropertyValues(mpvs);
}
protected void applyPropertyValues(MutablePropertyValues mpvs) {
try {
// Bind request parameters onto target object.
getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());
}
catch (PropertyBatchUpdateException ex) {
// Use bind error processor to create FieldErrors.
for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) {
getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());
}
}
}
//获取BeanWrapperImpl对象
protected ConfigurablePropertyAccessor getPropertyAccessor() {
return getInternalBindingResult().getPropertyAccessor();
}
参数赋值的具体操作在AbstractPropertyAccessor#setPropertyValues方法。
WebDataBinder
DataBinder的子类,主要是针对Web请求的数据绑定。
WebDataBinder的工厂方法创建
public class DefaultDataBinderFactory implements WebDataBinderFactory {
public final WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)
throws Exception {
WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
if (this.initializer != null) {
this.initializer.initBinder(dataBinder, webRequest);
}
initBinder(dataBinder, webRequest);
return dataBinder;
}
protected WebDataBinder createBinderInstance(Object target, String objectName, NativeWebRequest webRequest)
throws Exception {
return new WebRequestDataBinder(target, objectName);
}
}
WebRequestDataBinder
WebDataBinder的子类,将request中的参数进行绑定操作;同时也可对多文件上传进行绑定操作。
public class WebRequestDataBinder extends WebDataBinder {
public void bind(WebRequest request) {
MutablePropertyValues mpvs = new MutablePropertyValues(request.getParameterMap());
if (isMultipartRequest(request) && request instanceof NativeWebRequest) {
MultipartRequest multipartRequest = ((NativeWebRequest) request).getNativeRequest(MultipartRequest.class);
if (multipartRequest != null) {
bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
}
else if (servlet3Parts) {
HttpServletRequest serlvetRequest = ((NativeWebRequest) request).getNativeRequest(HttpServletRequest.class);
new Servlet3MultipartHelper(isBindEmptyMultipartFiles()).bindParts(serlvetRequest, mpvs);
}
}
doBind(mpvs);
}
}