Shared DefaultConversionService instance for simple fallback purposes

Issue: SPR-14948
This commit is contained in:
Juergen Hoeller 2016-11-24 15:29:17 +01:00
parent 34c6c9ffc2
commit 80931b211c
7 changed files with 82 additions and 19 deletions

View File

@ -25,12 +25,12 @@ import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.ConverterRegistry;
/**
* A specialization of {@link GenericConversionService} configured by default with
* converters appropriate for most environments.
* A specialization of {@link GenericConversionService} configured by default
* with converters appropriate for most environments.
*
* <p>Designed for direct instantiation but also exposes the static
* {@link #addDefaultConverters(ConverterRegistry)} utility method for ad hoc use against any
* {@code ConverterRegistry} instance.
* {@link #addDefaultConverters(ConverterRegistry)} utility method for ad-hoc
* use against any {@code ConverterRegistry} instance.
*
* @author Chris Beams
* @author Juergen Hoeller
@ -39,6 +39,32 @@ import org.springframework.core.convert.converter.ConverterRegistry;
*/
public class DefaultConversionService extends GenericConversionService {
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;
}
/**
* Create a new {@code DefaultConversionService} with the set of
* {@linkplain DefaultConversionService#addDefaultConverters(ConverterRegistry) default converters}.

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);
}
}
}
@ -218,6 +235,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

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

View File

@ -36,8 +36,6 @@ import org.springframework.util.Assert;
*/
public class StandardTypeConverter implements TypeConverter {
private static volatile ConversionService defaultConversionService;
private final ConversionService conversionService;
@ -45,10 +43,7 @@ public class StandardTypeConverter implements TypeConverter {
* Create a StandardTypeConverter for the default ConversionService.
*/
public StandardTypeConverter() {
if (defaultConversionService == null) {
defaultConversionService = new DefaultConversionService();
}
this.conversionService = defaultConversionService;
this.conversionService = DefaultConversionService.getSharedInstance();
}
/**

View File

@ -88,7 +88,7 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
private boolean primitivesDefaultedForNullValue = false;
/** ConversionService for binding JDBC values to bean properties */
private ConversionService conversionService = new DefaultConversionService();
private ConversionService conversionService = DefaultConversionService.getSharedInstance();
/** Map of the fields we provide mapping for */
private Map<String, PropertyDescriptor> mappedFields;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2016 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.
@ -44,7 +44,7 @@ public class GenericMessageConverter extends SimpleMessageConverter {
* Create a new instance with a default {@link ConversionService}.
*/
public GenericMessageConverter() {
this.conversionService = new DefaultConversionService();
this.conversionService = DefaultConversionService.getSharedInstance();
}
/**

View File

@ -76,7 +76,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
* values are not expected to contain expressions
*/
protected AbstractNamedValueMethodArgumentResolver(ConversionService cs, ConfigurableBeanFactory beanFactory) {
this.conversionService = (cs != null ? cs : new DefaultConversionService());
this.conversionService = (cs != null ? cs : DefaultConversionService.getSharedInstance());
this.configurableBeanFactory = beanFactory;
this.expressionContext = (beanFactory != null ? new BeanExpressionContext(beanFactory, null) : null);
}