Merge branch '2.5.x'

Closes gh-27831
This commit is contained in:
Phillip Webb 2021-08-27 22:49:39 -07:00
commit 8db82f02fc
2 changed files with 57 additions and 32 deletions

View File

@ -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;
}
}

View File

@ -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);
}
}
}