revised core conversion package for BeanWrapper/BeanFactory integration

This commit is contained in:
Juergen Hoeller
2009-08-09 00:46:49 +00:00
parent e9823b57b4
commit 45a0cadf8e
70 changed files with 1265 additions and 1113 deletions

View File

@@ -21,6 +21,8 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.springframework.core.convert.ConversionService;
/**
* Abstract implementation of the {@link PropertyAccessor} interface.
* Provides base implementations of all convenience methods, with the

View File

@@ -38,6 +38,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionException;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
@@ -145,7 +146,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
* Create new BeanWrapperImpl, wrapping a new instance of the specified class.
* @param clazz class to instantiate and wrap
*/
public BeanWrapperImpl(Class clazz) {
public BeanWrapperImpl(Class<?> clazz) {
registerDefaultEditors();
setWrappedInstance(BeanUtils.instantiateClass(clazz));
}
@@ -365,8 +366,8 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
return false;
}
public Object convertIfNecessary(
Object value, Class requiredType, MethodParameter methodParam) throws TypeMismatchException {
public <T> T convertIfNecessary(
Object value, Class<T> requiredType, MethodParameter methodParam) throws TypeMismatchException {
try {
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
}
@@ -572,7 +573,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
}
}
Object value = null;
Object value;
if (System.getSecurityManager() != null) {
try {
@@ -580,8 +581,9 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
public Object run() throws Exception {
return readMethod.invoke(object, (Object[]) null);
}
},acc);
} catch (PrivilegedActionException pae) {
}, acc);
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
@@ -625,7 +627,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
}
else if (value instanceof Map) {
Map map = (Map) value;
Class mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType(pd.getReadMethod(), i + 1);
Class<?> mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType(pd.getReadMethod(), i + 1);
// IMPORTANT: Do not pass full property name in here - property editors
// must not kick in for map keys but rather only for map values.
Object convertedMapKey = this.typeConverterDelegate.convertIfNecessary(key, mapKeyType);
@@ -945,6 +947,11 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
throw new MethodInvocationException(propertyChangeEvent, ex.getTargetException());
}
}
catch (ConversionException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
throw new TypeMismatchException(pce, pd.getPropertyType(), ex);
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
package org.springframework.beans;
import org.springframework.core.convert.ConversionService;
/**
* Interface that encapsulates configuration methods for a PropertyAccessor.
* Also extends the PropertyEditorRegistry interface, which defines methods
@@ -29,6 +31,17 @@ package org.springframework.beans;
*/
public interface ConfigurablePropertyAccessor extends PropertyAccessor, PropertyEditorRegistry, TypeConverter {
/**
* Specify a Spring 3.0 ConversionService to use for converting
* property values, as an alternative to JavaBeans PropertyEditors.
*/
void setConversionService(ConversionService conversionService);
/**
* Return the associated ConversionService, if any.
*/
ConversionService getConversionService();
/**
* Set whether to extract the old property value when applying a
* property editor to a new value for a property.

View File

@@ -57,6 +57,7 @@ import org.springframework.beans.propertyeditors.PropertiesEditor;
import org.springframework.beans.propertyeditors.StringArrayPropertyEditor;
import org.springframework.beans.propertyeditors.URIEditor;
import org.springframework.beans.propertyeditors.URLEditor;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourceArrayPropertyEditor;
import org.springframework.util.ClassUtils;
@@ -75,6 +76,8 @@ import org.springframework.util.ClassUtils;
*/
public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
private ConversionService conversionService;
private boolean defaultEditorsActive = false;
private boolean configValueEditorsActive = false;
@@ -90,6 +93,22 @@ public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
private Map<Class, PropertyEditor> customEditorCache;
/**
* Specify a Spring 3.0 ConversionService to use for converting
* property values, as an alternative to JavaBeans PropertyEditors.
*/
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
/**
* Return the associated ConversionService, if any.
*/
public ConversionService getConversionService() {
return this.conversionService;
}
//---------------------------------------------------------------------
// Management of default editors
//---------------------------------------------------------------------

View File

@@ -30,6 +30,8 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.core.CollectionFactory;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@@ -160,6 +162,18 @@ class TypeConverterDelegate {
// Custom editor for this type?
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
// No custom editor but custom ConversionService specified?
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && convertedValue != null &&
conversionService.canConvert(convertedValue.getClass(), requiredType)) {
if (methodParam != null) {
return (T) conversionService.convert(convertedValue, new TypeDescriptor(methodParam));
}
else {
return conversionService.convert(convertedValue, requiredType);
}
}
// Value not of required type?
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
if (editor == null) {
@@ -265,7 +279,7 @@ class TypeConverterDelegate {
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
protected Object doConvertValue(Object oldValue, Object newValue, Class requiredType, PropertyEditor editor) {
protected Object doConvertValue(Object oldValue, Object newValue, Class<?> requiredType, PropertyEditor editor) {
Object convertedValue = newValue;
boolean sharedEditor = false;
@@ -307,6 +321,8 @@ class TypeConverterDelegate {
}
}
Object returnValue = convertedValue;
if (requiredType != null && !requiredType.isArray() && convertedValue instanceof String[]) {
// Convert String array to a comma-separated String.
// Only applies if no PropertyEditor converted the String array before.
@@ -335,7 +351,7 @@ class TypeConverterDelegate {
}
}
return convertedValue;
return returnValue;
}
/**

View File

@@ -56,10 +56,10 @@ public class TypeMismatchException extends PropertyAccessException {
*/
public TypeMismatchException(PropertyChangeEvent propertyChangeEvent, Class requiredType, Throwable cause) {
super(propertyChangeEvent,
"Failed to convert property value of type [" +
ClassUtils.getDescriptiveType(propertyChangeEvent.getNewValue()) + "]" +
"Failed to convert property value of type '" +
ClassUtils.getDescriptiveType(propertyChangeEvent.getNewValue()) + "'" +
(requiredType != null ?
" to required type [" + ClassUtils.getQualifiedName(requiredType) + "]" : "") +
" to required type '" + ClassUtils.getQualifiedName(requiredType) + "'" : "") +
(propertyChangeEvent.getPropertyName() != null ?
" for property '" + propertyChangeEvent.getPropertyName() + "'" : ""),
cause);
@@ -83,8 +83,8 @@ public class TypeMismatchException extends PropertyAccessException {
* @param cause the root cause (may be <code>null</code>)
*/
public TypeMismatchException(Object value, Class requiredType, Throwable cause) {
super("Failed to convert value of type [" + ClassUtils.getDescriptiveType(value) + "]" +
(requiredType != null ? " to required type [" + ClassUtils.getQualifiedName(requiredType) + "]" : ""),
super("Failed to convert value of type '" + ClassUtils.getDescriptiveType(value) + "'" +
(requiredType != null ? " to required type '" + ClassUtils.getQualifiedName(requiredType) + "'" : ""),
cause);
this.value = value;
this.requiredType = requiredType;

View File

@@ -25,6 +25,7 @@ import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.core.convert.ConversionService;
import org.springframework.util.StringValueResolver;
/**
@@ -134,6 +135,17 @@ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, Single
*/
BeanExpressionResolver getBeanExpressionResolver();
/**
* Specify a Spring 3.0 ConversionService to use for converting
* property values, as an alternative to JavaBeans PropertyEditors.
*/
void setConversionService(ConversionService conversionService);
/**
* Return the associated ConversionService, if any.
*/
ConversionService getConversionService();
/**
* Add a PropertyEditorRegistrar to be applied to all bean creation processes.
* <p>Such a registrar creates new PropertyEditor instances and registers them

View File

@@ -66,6 +66,7 @@ import org.springframework.beans.factory.config.InstantiationAwareBeanPostProces
import org.springframework.beans.factory.config.Scope;
import org.springframework.core.DecoratingClassLoader;
import org.springframework.core.NamedThreadLocal;
import org.springframework.core.convert.ConversionService;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@@ -120,6 +121,9 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
/** Resolution strategy for expressions in bean definition values */
private BeanExpressionResolver beanExpressionResolver;
/** Spring 3.0 ConversionService to use instead of PropertyEditors */
private ConversionService conversionService;
/** Custom PropertyEditorRegistrars to apply to the beans of this factory */
private final Set<PropertyEditorRegistrar> propertyEditorRegistrars =
new LinkedHashSet<PropertyEditorRegistrar>(4);
@@ -620,6 +624,14 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
return this.beanExpressionResolver;
}
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
public ConversionService getConversionService() {
return this.conversionService;
}
public void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar) {
Assert.notNull(registrar, "PropertyEditorRegistrar must not be null");
this.propertyEditorRegistrars.add(registrar);
@@ -669,6 +681,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
else {
// Build default TypeConverter, registering custom editors.
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
typeConverter.setConversionService(getConversionService());
registerCustomEditors(typeConverter);
return typeConverter;
}
@@ -937,6 +950,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
* @param bw the BeanWrapper to initialize
*/
protected void initBeanWrapper(BeanWrapper bw) {
bw.setConversionService(getConversionService());
registerCustomEditors(bw);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,13 +37,6 @@ import java.util.Properties;
* @see java.util.Properties#load
*/
public class PropertiesEditor extends PropertyEditorSupport {
/**
* Any of these characters, if they're first after whitespace or first
* on a line, mean that the line is a comment and should be ignored.
*/
private final static String COMMENT_MARKERS = "#!";
/**
* Convert {@link String} into {@link Properties}, considering it as