Skip to content

Commit a7ba63d

Browse files
committed
Shared DefaultConversionService instance for simple fallback purposes
Issue: SPR-14948 (cherry picked from commit 80931b2)
1 parent 5ca10b1 commit a7ba63d

File tree

7 files changed

+77
-17
lines changed

7 files changed

+77
-17
lines changed

spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,30 @@ public class DefaultConversionService extends GenericConversionService {
5252
private static final boolean streamAvailable = ClassUtils.isPresent(
5353
"java.util.stream.Stream", DefaultConversionService.class.getClassLoader());
5454

55+
private static volatile DefaultConversionService sharedInstance;
56+
57+
58+
/**
59+
* Return a shared default {@code ConversionService} instance,
60+
* lazily building it once needed.
61+
* <p><b>NOTE:</b> We highly recommend constructing individual
62+
* {@code ConversionService} instances for customization purposes.
63+
* This accessor is only meant as a fallback for code paths which
64+
* need simple type coercion but cannot access a longer-lived
65+
* {@code ConversionService} instance any other way.
66+
* @return the shared {@code ConversionService} instance (never {@code null})
67+
* @since 4.3.5
68+
*/
69+
public static ConversionService getSharedInstance() {
70+
if (sharedInstance == null) {
71+
synchronized (DefaultConversionService.class) {
72+
if (sharedInstance == null) {
73+
sharedInstance = new DefaultConversionService();
74+
}
75+
}
76+
}
77+
return sharedInstance;
78+
}
5579

5680

5781
/**

spring-core/src/main/java/org/springframework/core/env/AbstractPropertyResolver.java

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@
2222
import org.apache.commons.logging.Log;
2323
import org.apache.commons.logging.LogFactory;
2424

25+
import org.springframework.core.convert.ConversionService;
2526
import org.springframework.core.convert.support.ConfigurableConversionService;
2627
import org.springframework.core.convert.support.DefaultConversionService;
28+
import org.springframework.util.Assert;
29+
import org.springframework.util.ClassUtils;
2730
import org.springframework.util.PropertyPlaceholderHelper;
2831
import org.springframework.util.SystemPropertyUtils;
2932

@@ -38,7 +41,7 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
3841

3942
protected final Log logger = LogFactory.getLog(getClass());
4043

41-
protected ConfigurableConversionService conversionService = new DefaultConversionService();
44+
private volatile ConfigurableConversionService conversionService;
4245

4346
private PropertyPlaceholderHelper nonStrictHelper;
4447

@@ -57,11 +60,21 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
5760

5861
@Override
5962
public ConfigurableConversionService getConversionService() {
60-
return this.conversionService;
63+
// Need to provide an independent DefaultConversionService, not the
64+
// shared DefaultConversionService used by PropertySourcesPropertyResolver.
65+
if (this.conversionService == null) {
66+
synchronized (this) {
67+
if (this.conversionService == null) {
68+
this.conversionService = new DefaultConversionService();
69+
}
70+
}
71+
}
72+
return conversionService;
6173
}
6274

6375
@Override
6476
public void setConversionService(ConfigurableConversionService conversionService) {
77+
Assert.notNull(conversionService, "ConversionService must not be null");
6578
this.conversionService = conversionService;
6679
}
6780

@@ -72,6 +85,7 @@ public void setConversionService(ConfigurableConversionService conversionService
7285
*/
7386
@Override
7487
public void setPlaceholderPrefix(String placeholderPrefix) {
88+
Assert.notNull(placeholderPrefix, "'placeholderPrefix' must not be null");
7589
this.placeholderPrefix = placeholderPrefix;
7690
}
7791

@@ -82,6 +96,7 @@ public void setPlaceholderPrefix(String placeholderPrefix) {
8296
*/
8397
@Override
8498
public void setPlaceholderSuffix(String placeholderSuffix) {
99+
Assert.notNull(placeholderSuffix, "'placeholderSuffix' must not be null");
85100
this.placeholderSuffix = placeholderSuffix;
86101
}
87102

@@ -113,8 +128,10 @@ public void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNe
113128

114129
@Override
115130
public void setRequiredProperties(String... requiredProperties) {
116-
for (String key : requiredProperties) {
117-
this.requiredProperties.add(key);
131+
if (requiredProperties != null) {
132+
for (String key : requiredProperties) {
133+
this.requiredProperties.add(key);
134+
}
118135
}
119136
}
120137

@@ -224,6 +241,31 @@ public String resolvePlaceholder(String placeholderName) {
224241
});
225242
}
226243

244+
/**
245+
* Convert the given value to the specified target type, if necessary.
246+
* @param value the original property value
247+
* @param targetType the specified target type for property retrieval
248+
* @return the converted value, or the original value if no conversion
249+
* is necessary
250+
* @since 4.3.5
251+
*/
252+
@SuppressWarnings("unchecked")
253+
protected <T> T convertValueIfNecessary(Object value, Class<T> targetType) {
254+
if (targetType == null) {
255+
return (T) value;
256+
}
257+
ConversionService csToUse = this.conversionService;
258+
if (csToUse == null) {
259+
// Avoid initialization of shared DefaultConversionService if
260+
// no standard type conversion is needed in the first place...
261+
if (ClassUtils.isAssignableValue(targetType, value)) {
262+
return (T) value;
263+
}
264+
csToUse = DefaultConversionService.getSharedInstance();
265+
}
266+
return csToUse.convert(value, targetType);
267+
}
268+
227269

228270
/**
229271
* Retrieve the specified property as a raw String,

spring-core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolv
8383
value = resolveNestedPlaceholders((String) value);
8484
}
8585
logKeyFound(key, propertySource, value);
86-
return this.conversionService.convert(value, targetValueType);
86+
return convertValueIfNecessary(value, targetValueType);
8787
}
8888
}
8989
}

spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,14 @@
3636
*/
3737
public class StandardTypeConverter implements TypeConverter {
3838

39-
private static volatile ConversionService defaultConversionService;
40-
4139
private final ConversionService conversionService;
4240

4341

4442
/**
4543
* Create a StandardTypeConverter for the default ConversionService.
4644
*/
4745
public StandardTypeConverter() {
48-
if (defaultConversionService == null) {
49-
defaultConversionService = new DefaultConversionService();
50-
}
51-
this.conversionService = defaultConversionService;
46+
this.conversionService = DefaultConversionService.getSharedInstance();
5247
}
5348

5449
/**

spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
8888
private boolean primitivesDefaultedForNullValue = false;
8989

9090
/** ConversionService for binding JDBC values to bean properties */
91-
private ConversionService conversionService = new DefaultConversionService();
91+
private ConversionService conversionService = DefaultConversionService.getSharedInstance();
9292

9393
/** Map of the fields we provide mapping for */
9494
private Map<String, PropertyDescriptor> mappedFields;

spring-messaging/src/main/java/org/springframework/messaging/converter/GenericMessageConverter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -44,7 +44,7 @@ public class GenericMessageConverter extends SimpleMessageConverter {
4444
* Create a new instance with a default {@link ConversionService}.
4545
*/
4646
public GenericMessageConverter() {
47-
this.conversionService = new DefaultConversionService();
47+
this.conversionService = DefaultConversionService.getSharedInstance();
4848
}
4949

5050
/**

spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractNamedValueMethodArgumentResolver.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
7676
* values are not expected to contain expressions
7777
*/
7878
protected AbstractNamedValueMethodArgumentResolver(ConversionService cs, ConfigurableBeanFactory beanFactory) {
79-
this.conversionService = (cs != null ? cs : new DefaultConversionService());
79+
this.conversionService = (cs != null ? cs : DefaultConversionService.getSharedInstance());
8080
this.configurableBeanFactory = beanFactory;
8181
this.expressionContext = (beanFactory != null ? new BeanExpressionContext(beanFactory, null) : null);
8282
}
@@ -108,8 +108,7 @@ else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
108108
}
109109

110110
if (!ClassUtils.isAssignableValue(parameter.getParameterType(), arg)) {
111-
arg = this.conversionService.convert(
112-
arg, TypeDescriptor.valueOf(arg.getClass()), new TypeDescriptor(parameter));
111+
arg = this.conversionService.convert(arg, TypeDescriptor.forObject(arg), new TypeDescriptor(parameter));
113112
}
114113

115114
handleResolvedValue(arg, namedValueInfo.name, parameter, message);

0 commit comments

Comments
 (0)