Skip to content

Commit e5906a6

Browse files
committed
Allow HttpMsgConverter to depend on ConvService without creating a cycle
In an MVC web application, DelegatingWebMvcConfiguration provides the ConversionService while also consuming WebMvcConfigurerAdapters that, among other things, can configure HTTP message converters. Boot's WebMvcConfigurerAdapter, WebMvcAutoConfigurationAdapter, consumes the HttpMessageConverters bean and uses it to configure Spring MVC's HTTP message converters. This can create a bean dependency cycle if an HTTP message converter bean depends, directly or indirectly on the ConversionService. An example of the cycle is: ┌─────┐ | jsonComponentConversionServiceCycle.ThingDeserializer defined in … ↑ ↓ | org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$EnableWebMvcConfiguration ↑ ↓ | org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter ↑ ↓ | org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration ↑ ↓ | mappingJackson2HttpMessageConverter defined in class path resource [org/springframework/boot/autoconfigure/web/JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration.class] ↑ ↓ | jacksonObjectMapper defined in class path resource [org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration$JacksonObjectMapperConfiguration.class] └─────┘ This commit breaks the cycle by making WebMvcAutoConfigurationAdapter consume HttpMessageConverters lazily. This allows the adapter to be created without triggered instantiation of every HTTP message converter bean and all their dependencies. This allows it to be injected into DelegatingWebMvcConfiguration without triggering an attempt to retrieve the ConversionService. Closes gh-9409
1 parent 6b7dfce commit e5906a6

File tree

2 files changed

+20
-1
lines changed

2 files changed

+20
-1
lines changed

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.springframework.context.annotation.Bean;
5353
import org.springframework.context.annotation.Configuration;
5454
import org.springframework.context.annotation.Import;
55+
import org.springframework.context.annotation.Lazy;
5556
import org.springframework.context.annotation.Primary;
5657
import org.springframework.core.Ordered;
5758
import org.springframework.core.convert.converter.Converter;
@@ -170,7 +171,7 @@ public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapt
170171

171172
public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties,
172173
WebMvcProperties mvcProperties, ListableBeanFactory beanFactory,
173-
HttpMessageConverters messageConverters,
174+
@Lazy HttpMessageConverters messageConverters,
174175
ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
175176
this.resourceProperties = resourceProperties;
176177
this.mvcProperties = mvcProperties;

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfigurationTests.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,13 @@
5151
import org.springframework.context.annotation.Bean;
5252
import org.springframework.context.annotation.Configuration;
5353
import org.springframework.context.annotation.Import;
54+
import org.springframework.core.convert.ConversionService;
5455
import org.springframework.core.io.ClassPathResource;
5556
import org.springframework.core.io.Resource;
5657
import org.springframework.format.support.FormattingConversionService;
5758
import org.springframework.http.HttpHeaders;
5859
import org.springframework.http.MediaType;
60+
import org.springframework.http.converter.HttpMessageConverter;
5961
import org.springframework.mock.web.MockHttpServletRequest;
6062
import org.springframework.test.util.ReflectionTestUtils;
6163
import org.springframework.test.web.servlet.MockMvc;
@@ -760,6 +762,11 @@ public void validatorWithCustomJsr303ValidatorExposedAsSpringValidator() {
760762
.isSameAs(this.context.getBean("customJsr303Validator"));
761763
}
762764

765+
@Test
766+
public void httpMessageConverterThatUsesConversionServiceDoesNotCreateACycle() {
767+
load(CustomHttpMessageConverter.class);
768+
}
769+
763770
private void load(Class<?> config, String... environment) {
764771
load(config, null, environment);
765772
}
@@ -990,4 +997,15 @@ public Validator customSpringValidator() {
990997

991998
}
992999

1000+
@Configuration
1001+
static class CustomHttpMessageConverter {
1002+
1003+
@Bean
1004+
public HttpMessageConverter<?> customHttpMessageConverter(
1005+
ConversionService conversionService) {
1006+
return mock(HttpMessageConverter.class);
1007+
}
1008+
1009+
}
1010+
9931011
}

0 commit comments

Comments
 (0)