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 1530b8daad..621a7f91f5 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 @@ -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. * *
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. + *
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;
+ }
+
+
/**
* Create a new {@code DefaultConversionService} with the set of
* {@linkplain DefaultConversionService#addDefaultConverters(ConverterRegistry) default converters}.
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 20d690b5a0..3bdf2b0489 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);
+ }
}
}
@@ -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