Commit 5581ec0e authored by Phillip Webb's avatar Phillip Webb

Make getSharedInstance() unmodifiable

Update `ApplicationConversionService.getSharedInstance()` so that the
instance returned is unmodifiable and converters cannot be added or
removed from it.

Closes gh-26088
parent 47709ec0
...@@ -60,8 +60,6 @@ import org.springframework.context.support.AbstractApplicationContext; ...@@ -60,8 +60,6 @@ import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericApplicationContext; import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.GenericTypeResolver; import org.springframework.core.GenericTypeResolver;
import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.env.CommandLinePropertySource; import org.springframework.core.env.CommandLinePropertySource;
import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
...@@ -512,8 +510,7 @@ public class SpringApplication { ...@@ -512,8 +510,7 @@ public class SpringApplication {
*/ */
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) { if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance(); environment.setConversionService(new ApplicationConversionService());
environment.setConversionService((ConfigurableConversionService) conversionService);
} }
configurePropertySources(environment, args); configurePropertySources(environment, args);
configureProfiles(environment, args); configureProfiles(environment, args);
...@@ -633,7 +630,7 @@ public class SpringApplication { ...@@ -633,7 +630,7 @@ public class SpringApplication {
} }
} }
if (this.addConversionService) { if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance()); context.getBeanFactory().setConversionService(context.getEnvironment().getConversionService());
} }
} }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package org.springframework.boot.convert; package org.springframework.boot.convert;
import java.lang.annotation.Annotation;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Set; import java.util.Set;
...@@ -23,11 +24,13 @@ import org.springframework.beans.factory.ListableBeanFactory; ...@@ -23,11 +24,13 @@ import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.converter.ConverterRegistry; import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.core.convert.converter.GenericConverter; import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair; import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
import org.springframework.core.convert.support.ConfigurableConversionService; import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.format.AnnotationFormatterFactory;
import org.springframework.format.Formatter; import org.springframework.format.Formatter;
import org.springframework.format.FormatterRegistry; import org.springframework.format.FormatterRegistry;
import org.springframework.format.Parser; import org.springframework.format.Parser;
...@@ -52,15 +55,96 @@ public class ApplicationConversionService extends FormattingConversionService { ...@@ -52,15 +55,96 @@ public class ApplicationConversionService extends FormattingConversionService {
private static volatile ApplicationConversionService sharedInstance; private static volatile ApplicationConversionService sharedInstance;
private boolean unmodifiable;
public ApplicationConversionService() { public ApplicationConversionService() {
this(null); this(null);
} }
public ApplicationConversionService(StringValueResolver embeddedValueResolver) { public ApplicationConversionService(StringValueResolver embeddedValueResolver) {
this(embeddedValueResolver, false);
}
private ApplicationConversionService(StringValueResolver embeddedValueResolver, boolean unmodifiable) {
if (embeddedValueResolver != null) { if (embeddedValueResolver != null) {
setEmbeddedValueResolver(embeddedValueResolver); setEmbeddedValueResolver(embeddedValueResolver);
} }
configure(this); configure(this);
this.unmodifiable = unmodifiable;
}
@Override
public void addPrinter(Printer<?> printer) {
assertModifiable();
super.addPrinter(printer);
}
@Override
public void addParser(Parser<?> parser) {
assertModifiable();
super.addParser(parser);
}
@Override
public void addFormatter(Formatter<?> formatter) {
assertModifiable();
super.addFormatter(formatter);
}
@Override
public void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter) {
assertModifiable();
super.addFormatterForFieldType(fieldType, formatter);
}
@Override
public void addConverter(Converter<?, ?> converter) {
assertModifiable();
super.addConverter(converter);
}
@Override
public void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser) {
assertModifiable();
super.addFormatterForFieldType(fieldType, printer, parser);
}
@Override
public void addFormatterForFieldAnnotation(
AnnotationFormatterFactory<? extends Annotation> annotationFormatterFactory) {
assertModifiable();
super.addFormatterForFieldAnnotation(annotationFormatterFactory);
}
@Override
public <S, T> void addConverter(Class<S> sourceType, Class<T> targetType,
Converter<? super S, ? extends T> converter) {
assertModifiable();
super.addConverter(sourceType, targetType, converter);
}
@Override
public void addConverter(GenericConverter converter) {
assertModifiable();
super.addConverter(converter);
}
@Override
public void addConverterFactory(ConverterFactory<?, ?> factory) {
assertModifiable();
super.addConverterFactory(factory);
}
@Override
public void removeConvertible(Class<?> sourceType, Class<?> targetType) {
assertModifiable();
super.removeConvertible(sourceType, targetType);
}
private void assertModifiable() {
if (this.unmodifiable) {
throw new UnsupportedOperationException("This ApplicationConversionService cannot be modified");
}
} }
/** /**
...@@ -101,7 +185,7 @@ public class ApplicationConversionService extends FormattingConversionService { ...@@ -101,7 +185,7 @@ public class ApplicationConversionService extends FormattingConversionService {
synchronized (ApplicationConversionService.class) { synchronized (ApplicationConversionService.class) {
sharedInstance = ApplicationConversionService.sharedInstance; sharedInstance = ApplicationConversionService.sharedInstance;
if (sharedInstance == null) { if (sharedInstance == null) {
sharedInstance = new ApplicationConversionService(); sharedInstance = new ApplicationConversionService(null, true);
ApplicationConversionService.sharedInstance = sharedInstance; ApplicationConversionService.sharedInstance = sharedInstance;
} }
} }
......
...@@ -21,6 +21,7 @@ import java.util.List; ...@@ -21,6 +21,7 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
...@@ -34,6 +35,7 @@ import org.springframework.format.Parser; ...@@ -34,6 +35,7 @@ import org.springframework.format.Parser;
import org.springframework.format.Printer; import org.springframework.format.Printer;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
...@@ -113,6 +115,28 @@ class ApplicationConversionServiceTests { ...@@ -113,6 +115,28 @@ class ApplicationConversionServiceTests {
assertThat(conversionService.isConvertViaObjectSourceType(sourceType, targetType)).isFalse(); assertThat(conversionService.isConvertViaObjectSourceType(sourceType, targetType)).isFalse();
} }
@Test
void sharedInstanceCannotBeModified() {
ApplicationConversionService instance = (ApplicationConversionService) ApplicationConversionService
.getSharedInstance();
assertUnmodifiableExceptionThrown(() -> instance.addPrinter(null));
assertUnmodifiableExceptionThrown(() -> instance.addParser(null));
assertUnmodifiableExceptionThrown(() -> instance.addFormatter(null));
assertUnmodifiableExceptionThrown(() -> instance.addFormatterForFieldType(null, null));
assertUnmodifiableExceptionThrown(() -> instance.addConverter((Converter<?, ?>) null));
assertUnmodifiableExceptionThrown(() -> instance.addFormatterForFieldType(null, null, null));
assertUnmodifiableExceptionThrown(() -> instance.addFormatterForFieldAnnotation(null));
assertUnmodifiableExceptionThrown(() -> instance.addConverter(null, null, null));
assertUnmodifiableExceptionThrown(() -> instance.addConverter((GenericConverter) null));
assertUnmodifiableExceptionThrown(() -> instance.addConverterFactory(null));
assertUnmodifiableExceptionThrown(() -> instance.removeConvertible(null, null));
}
private void assertUnmodifiableExceptionThrown(ThrowingCallable throwingCallable) {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(throwingCallable)
.withMessage("This ApplicationConversionService cannot be modified");
}
static class ExampleGenericConverter implements GenericConverter { static class ExampleGenericConverter implements GenericConverter {
@Override @Override
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment