commit
8db82f02fc
|
|
@ -152,18 +152,10 @@ final class BindConverter {
|
|||
private static class TypeConverterConversionService extends GenericConversionService {
|
||||
|
||||
TypeConverterConversionService(Consumer<PropertyEditorRegistry> initializer) {
|
||||
addConverter(new TypeConverterConverter(createTypeConverter(initializer)));
|
||||
addConverter(new TypeConverterConverter(initializer));
|
||||
ApplicationConversionService.addDelimitedStringConverters(this);
|
||||
}
|
||||
|
||||
private SimpleTypeConverter createTypeConverter(Consumer<PropertyEditorRegistry> initializer) {
|
||||
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
|
||||
if (initializer != null) {
|
||||
initializer.accept(typeConverter);
|
||||
}
|
||||
return typeConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
// Prefer conversion service to handle things like String to char[].
|
||||
|
|
@ -187,10 +179,15 @@ final class BindConverter {
|
|||
EXCLUDED_EDITORS = Collections.unmodifiableSet(excluded);
|
||||
}
|
||||
|
||||
private final SimpleTypeConverter typeConverter;
|
||||
private final Consumer<PropertyEditorRegistry> initializer;
|
||||
|
||||
TypeConverterConverter(SimpleTypeConverter typeConverter) {
|
||||
this.typeConverter = typeConverter;
|
||||
// SimpleTypeConverter is not thread-safe to use for conversion but we can use it
|
||||
// in a thread-safe way to check if conversion is possible.
|
||||
private final SimpleTypeConverter matchesOnlyTypeConverter;
|
||||
|
||||
TypeConverterConverter(Consumer<PropertyEditorRegistry> initializer) {
|
||||
this.initializer = initializer;
|
||||
this.matchesOnlyTypeConverter = createTypeConverter();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -200,32 +197,32 @@ final class BindConverter {
|
|||
|
||||
@Override
|
||||
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return getPropertyEditor(targetType.getType()) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
SimpleTypeConverter typeConverter = this.typeConverter;
|
||||
return typeConverter.convertIfNecessary(source, targetType.getType());
|
||||
}
|
||||
|
||||
private PropertyEditor getPropertyEditor(Class<?> type) {
|
||||
Class<?> type = targetType.getType();
|
||||
if (type == null || type == Object.class || Collection.class.isAssignableFrom(type)
|
||||
|| Map.class.isAssignableFrom(type)) {
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
SimpleTypeConverter typeConverter = this.typeConverter;
|
||||
PropertyEditor editor = typeConverter.getDefaultEditor(type);
|
||||
PropertyEditor editor = this.matchesOnlyTypeConverter.getDefaultEditor(type);
|
||||
if (editor == null) {
|
||||
editor = typeConverter.findCustomEditor(type, null);
|
||||
editor = this.matchesOnlyTypeConverter.findCustomEditor(type, null);
|
||||
}
|
||||
if (editor == null && String.class != type) {
|
||||
editor = BeanUtils.findEditorByConvention(type);
|
||||
}
|
||||
if (editor == null || EXCLUDED_EDITORS.contains(editor.getClass())) {
|
||||
return null;
|
||||
return (editor != null && !EXCLUDED_EDITORS.contains(editor.getClass()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return createTypeConverter().convertIfNecessary(source, targetType.getType());
|
||||
}
|
||||
|
||||
private SimpleTypeConverter createTypeConverter() {
|
||||
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
|
||||
if (this.initializer != null) {
|
||||
this.initializer.accept(typeConverter);
|
||||
}
|
||||
return editor;
|
||||
return typeConverter;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package org.springframework.boot.context.properties.bind;
|
|||
import java.beans.PropertyEditorSupport;
|
||||
import java.io.File;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
|
@ -188,6 +189,30 @@ class BindConverterTests {
|
|||
.withRootCauseInstanceOf(ClassNotFoundException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void convertWhenUsingTypeConverterConversionServiceFromMultipleThreads() {
|
||||
BindConverter bindConverter = getPropertyEditorOnlyBindConverter(this::registerSampleTypeEditor);
|
||||
ResolvableType type = ResolvableType.forClass(SampleType.class);
|
||||
List<Thread> threads = new ArrayList<>();
|
||||
List<SampleType> results = Collections.synchronizedList(new ArrayList<>());
|
||||
for (int i = 0; i < 40; i++) {
|
||||
threads.add(new Thread(() -> {
|
||||
for (int j = 0; j < 20; j++) {
|
||||
results.add(bindConverter.convert("test", type));
|
||||
}
|
||||
}));
|
||||
}
|
||||
threads.forEach(Thread::start);
|
||||
for (Thread thread : threads) {
|
||||
try {
|
||||
thread.join();
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
assertThat(results).isNotEmpty().doesNotContainNull();
|
||||
}
|
||||
|
||||
private BindConverter getPropertyEditorOnlyBindConverter(
|
||||
Consumer<PropertyEditorRegistry> propertyEditorInitializer) {
|
||||
return BindConverter.get(Collections.singletonList(new ThrowingConversionService()), propertyEditorInitializer);
|
||||
|
|
@ -217,9 +242,12 @@ class BindConverterTests {
|
|||
|
||||
@Override
|
||||
public void setAsText(String text) {
|
||||
SampleType value = new SampleType();
|
||||
value.text = text;
|
||||
setValue(value);
|
||||
setValue(null);
|
||||
if (text != null) {
|
||||
SampleType value = new SampleType();
|
||||
value.text = text;
|
||||
setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue