diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java b/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java
index 86df6337f6..af4e839093 100644
--- a/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java
+++ b/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java
@@ -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.
+ *
NOTE: 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;
+ }
/**
diff --git a/spring-core/src/main/java/org/springframework/core/env/AbstractPropertyResolver.java b/spring-core/src/main/java/org/springframework/core/env/AbstractPropertyResolver.java
index 1c001d962a..11c5ff7644 100644
--- a/spring-core/src/main/java/org/springframework/core/env/AbstractPropertyResolver.java
+++ b/spring-core/src/main/java/org/springframework/core/env/AbstractPropertyResolver.java
@@ -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 convertValueIfNecessary(Object value, Class 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,
diff --git a/spring-core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java b/spring-core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java
index 1a92673de2..a17596ee98 100644
--- a/spring-core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java
+++ b/spring-core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java
@@ -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);
}
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java
index 253e4bb389..6243c2d754 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java
@@ -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();
}
/**
diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java
index 6a0f51bb2b..929b40a110 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java
@@ -88,7 +88,7 @@ public class BeanPropertyRowMapper implements RowMapper {
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 mappedFields;
diff --git a/spring-messaging/src/main/java/org/springframework/messaging/converter/GenericMessageConverter.java b/spring-messaging/src/main/java/org/springframework/messaging/converter/GenericMessageConverter.java
index f9b5f4427a..df9e597a7f 100644
--- a/spring-messaging/src/main/java/org/springframework/messaging/converter/GenericMessageConverter.java
+++ b/spring-messaging/src/main/java/org/springframework/messaging/converter/GenericMessageConverter.java
@@ -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();
}
/**
diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractNamedValueMethodArgumentResolver.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractNamedValueMethodArgumentResolver.java
index dcf20f8381..21502c05d4 100644
--- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractNamedValueMethodArgumentResolver.java
+++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractNamedValueMethodArgumentResolver.java
@@ -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);
}
@@ -108,8 +108,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
}
if (!ClassUtils.isAssignableValue(parameter.getParameterType(), arg)) {
- arg = this.conversionService.convert(
- arg, TypeDescriptor.valueOf(arg.getClass()), new TypeDescriptor(parameter));
+ arg = this.conversionService.convert(arg, TypeDescriptor.forObject(arg), new TypeDescriptor(parameter));
}
handleResolvedValue(arg, namedValueInfo.name, parameter, message);