Shared DefaultConversionService instance for simple fallback purposes

Issue: SPR-14948
(cherry picked from commit 80931b2)
This commit is contained in:
Juergen Hoeller
2016-11-24 15:29:17 +01:00
parent 5ca10b1d67
commit a7ba63d425
7 changed files with 77 additions and 17 deletions

View File

@@ -52,6 +52,30 @@ public class DefaultConversionService extends GenericConversionService {
private static final boolean streamAvailable = ClassUtils.isPresent(
"java.util.stream.Stream", DefaultConversionService.class.getClassLoader());
private static volatile DefaultConversionService sharedInstance;
/**
* Return a shared default {@code ConversionService} instance,
* lazily building it once needed.
* <p><b>NOTE:</b> We highly recommend constructing individual
* {@code ConversionService} instances for customization purposes.
* This accessor is only meant as a fallback for code paths which
* need simple type coercion but cannot access a longer-lived
* {@code ConversionService} instance any other way.
* @return the shared {@code ConversionService} instance (never {@code null})
* @since 4.3.5
*/
public static ConversionService getSharedInstance() {
if (sharedInstance == null) {
synchronized (DefaultConversionService.class) {
if (sharedInstance == null) {
sharedInstance = new DefaultConversionService();
}
}
}
return sharedInstance;
}
/**

View File

@@ -22,8 +22,11 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.SystemPropertyUtils;
@@ -38,7 +41,7 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
protected final Log logger = LogFactory.getLog(getClass());
protected ConfigurableConversionService conversionService = new DefaultConversionService();
private volatile ConfigurableConversionService conversionService;
private PropertyPlaceholderHelper nonStrictHelper;
@@ -57,11 +60,21 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
@Override
public ConfigurableConversionService getConversionService() {
return this.conversionService;
// Need to provide an independent DefaultConversionService, not the
// shared DefaultConversionService used by PropertySourcesPropertyResolver.
if (this.conversionService == null) {
synchronized (this) {
if (this.conversionService == null) {
this.conversionService = new DefaultConversionService();
}
}
}
return conversionService;
}
@Override
public void setConversionService(ConfigurableConversionService conversionService) {
Assert.notNull(conversionService, "ConversionService must not be null");
this.conversionService = conversionService;
}
@@ -72,6 +85,7 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
*/
@Override
public void setPlaceholderPrefix(String placeholderPrefix) {
Assert.notNull(placeholderPrefix, "'placeholderPrefix' must not be null");
this.placeholderPrefix = placeholderPrefix;
}
@@ -82,6 +96,7 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
*/
@Override
public void setPlaceholderSuffix(String placeholderSuffix) {
Assert.notNull(placeholderSuffix, "'placeholderSuffix' must not be null");
this.placeholderSuffix = placeholderSuffix;
}
@@ -113,8 +128,10 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
@Override
public void setRequiredProperties(String... requiredProperties) {
for (String key : requiredProperties) {
this.requiredProperties.add(key);
if (requiredProperties != null) {
for (String key : requiredProperties) {
this.requiredProperties.add(key);
}
}
}
@@ -224,6 +241,31 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
});
}
/**
* Convert the given value to the specified target type, if necessary.
* @param value the original property value
* @param targetType the specified target type for property retrieval
* @return the converted value, or the original value if no conversion
* is necessary
* @since 4.3.5
*/
@SuppressWarnings("unchecked")
protected <T> T convertValueIfNecessary(Object value, Class<T> targetType) {
if (targetType == null) {
return (T) value;
}
ConversionService csToUse = this.conversionService;
if (csToUse == null) {
// Avoid initialization of shared DefaultConversionService if
// no standard type conversion is needed in the first place...
if (ClassUtils.isAssignableValue(targetType, value)) {
return (T) value;
}
csToUse = DefaultConversionService.getSharedInstance();
}
return csToUse.convert(value, targetType);
}
/**
* Retrieve the specified property as a raw String,

View File

@@ -83,7 +83,7 @@ public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {
value = resolveNestedPlaceholders((String) value);
}
logKeyFound(key, propertySource, value);
return this.conversionService.convert(value, targetValueType);
return convertValueIfNecessary(value, targetValueType);
}
}
}