-
Notifications
You must be signed in to change notification settings - Fork 38k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add qualified injection points for MVC and WebFlux infrastructure
Previously, the infrastructure provided by WebMvcConfigurationSupport and WebFluxConfigurationSupport can lead to unexpected results due to the lack of qualifier for certain dependencies. Those configuration classes refer to very specific beans, yet their injection points do not define such qualifiers. As a result, if a candidate exists for the requested type, the context will inject the existing bean and will ignore a most specific one as such constraint it not defined. This can be easily reproduced by having a primary Validator whereas a dedicated "mvcValidator" is expected. Note that a parameter name is in no way a constraint as the name is only used as a fallback when a single candidate cannot be determined. This commit provides explicit @qualifier metadata for such injection points, renaming the parameter name in the process to clarify that it isn't relevant for the proper bean to be resolved by the context. Closes gh-23887
- Loading branch information
Showing
4 changed files
with
460 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
199 changes: 199 additions & 0 deletions
199
...g/springframework/web/reactive/config/DelegatingWebFluxConfigurationIntegrationTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
/* | ||
* Copyright 2002-2019 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.springframework.web.reactive.config; | ||
|
||
import java.util.function.Consumer; | ||
|
||
import org.junit.jupiter.api.AfterEach; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import org.springframework.context.annotation.AnnotationConfigApplicationContext; | ||
import org.springframework.core.ReactiveAdapterRegistry; | ||
import org.springframework.format.support.FormattingConversionService; | ||
import org.springframework.validation.Validator; | ||
import org.springframework.web.reactive.accept.RequestedContentTypeResolver; | ||
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter; | ||
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping; | ||
import org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler; | ||
import org.springframework.web.reactive.result.method.annotation.ResponseEntityResultHandler; | ||
import org.springframework.web.reactive.result.view.ViewResolutionResultHandler; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.mockito.Mockito.mock; | ||
|
||
/** | ||
* Integration tests for {@link DelegatingWebFluxConfiguration}. | ||
* | ||
* @author Stephane Nicoll | ||
*/ | ||
public class DelegatingWebFluxConfigurationIntegrationTests { | ||
|
||
private AnnotationConfigApplicationContext context; | ||
|
||
@AfterEach | ||
public void closeContext() { | ||
if (this.context != null) { | ||
this.context.close(); | ||
} | ||
} | ||
|
||
@Test | ||
void requestMappingHandlerMappingUsesWebFluxInfrastructureByDefault() { | ||
load(context -> { }); | ||
RequestMappingHandlerMapping handlerMapping = this.context.getBean(RequestMappingHandlerMapping.class); | ||
assertThat(handlerMapping.getContentTypeResolver()).isSameAs(this.context.getBean("webFluxContentTypeResolver")); | ||
} | ||
|
||
@Test | ||
void requestMappingHandlerMappingWithPrimaryUsesQualifiedRequestedContentTypeResolver() { | ||
load(registerPrimaryBean("testContentTypeResolver", RequestedContentTypeResolver.class)); | ||
RequestMappingHandlerMapping handlerMapping = this.context.getBean(RequestMappingHandlerMapping.class); | ||
assertThat(handlerMapping.getContentTypeResolver()).isSameAs(this.context.getBean("webFluxContentTypeResolver")); | ||
assertThat(this.context.getBeansOfType(RequestedContentTypeResolver.class)).containsOnlyKeys( | ||
"webFluxContentTypeResolver", "testContentTypeResolver"); | ||
} | ||
|
||
@Test | ||
void requestMappingHandlerAdapterUsesWebFluxInfrastructureByDefault() { | ||
load(context -> { }); | ||
RequestMappingHandlerAdapter mappingHandlerAdapter = this.context.getBean(RequestMappingHandlerAdapter.class); | ||
assertThat(mappingHandlerAdapter.getReactiveAdapterRegistry()).isSameAs(this.context.getBean("webFluxAdapterRegistry")); | ||
assertThat(mappingHandlerAdapter.getWebBindingInitializer()).hasFieldOrPropertyWithValue("conversionService", | ||
this.context.getBean("webFluxConversionService")); | ||
assertThat(mappingHandlerAdapter.getWebBindingInitializer()).hasFieldOrPropertyWithValue("validator", | ||
this.context.getBean("webFluxValidator")); | ||
} | ||
|
||
@Test | ||
void requestMappingHandlerAdapterWithPrimaryUsesQualifiedReactiveAdapterRegistry() { | ||
load(registerPrimaryBean("testReactiveAdapterRegistry", ReactiveAdapterRegistry.class)); | ||
RequestMappingHandlerAdapter mappingHandlerAdapter = this.context.getBean(RequestMappingHandlerAdapter.class); | ||
assertThat(mappingHandlerAdapter.getReactiveAdapterRegistry()).isSameAs(this.context.getBean("webFluxAdapterRegistry")); | ||
assertThat(this.context.getBeansOfType(ReactiveAdapterRegistry.class)).containsOnlyKeys( | ||
"webFluxAdapterRegistry", "testReactiveAdapterRegistry"); | ||
} | ||
|
||
@Test | ||
void requestMappingHandlerAdapterWithPrimaryUsesQualifiedConversionService() { | ||
load(registerPrimaryBean("testConversionService", FormattingConversionService.class)); | ||
RequestMappingHandlerAdapter mappingHandlerAdapter = this.context.getBean(RequestMappingHandlerAdapter.class); | ||
assertThat(mappingHandlerAdapter.getWebBindingInitializer()).hasFieldOrPropertyWithValue("conversionService", | ||
this.context.getBean("webFluxConversionService")); | ||
assertThat(this.context.getBeansOfType(FormattingConversionService.class)).containsOnlyKeys( | ||
"webFluxConversionService", "testConversionService"); | ||
} | ||
|
||
@Test | ||
void requestMappingHandlerAdapterWithPrimaryUsesQualifiedValidator() { | ||
load(registerPrimaryBean("testValidator", Validator.class)); | ||
RequestMappingHandlerAdapter mappingHandlerAdapter = this.context.getBean(RequestMappingHandlerAdapter.class); | ||
assertThat(mappingHandlerAdapter.getWebBindingInitializer()).hasFieldOrPropertyWithValue("validator", | ||
this.context.getBean("webFluxValidator")); | ||
assertThat(this.context.getBeansOfType(Validator.class)).containsOnlyKeys( | ||
"webFluxValidator", "testValidator"); | ||
} | ||
|
||
@Test | ||
void responseEntityResultHandlerUsesWebFluxInfrastructureByDefault() { | ||
load(context -> { }); | ||
ResponseEntityResultHandler responseEntityResultHandler = this.context.getBean(ResponseEntityResultHandler.class); | ||
assertThat(responseEntityResultHandler.getAdapterRegistry()).isSameAs(this.context.getBean("webFluxAdapterRegistry")); | ||
assertThat(responseEntityResultHandler.getContentTypeResolver()).isSameAs(this.context.getBean("webFluxContentTypeResolver")); | ||
} | ||
|
||
@Test | ||
void responseEntityResultHandlerWithPrimaryUsesQualifiedReactiveAdapterRegistry() { | ||
load(registerPrimaryBean("testReactiveAdapterRegistry", ReactiveAdapterRegistry.class)); | ||
ResponseEntityResultHandler responseEntityResultHandler = this.context.getBean(ResponseEntityResultHandler.class); | ||
assertThat(responseEntityResultHandler.getAdapterRegistry()).isSameAs(this.context.getBean("webFluxAdapterRegistry")); | ||
assertThat(this.context.getBeansOfType(ReactiveAdapterRegistry.class)).containsOnlyKeys( | ||
"webFluxAdapterRegistry", "testReactiveAdapterRegistry"); | ||
} | ||
|
||
@Test | ||
void responseEntityResultHandlerWithPrimaryUsesQualifiedRequestedContentTypeResolver() { | ||
load(registerPrimaryBean("testContentTypeResolver", RequestedContentTypeResolver.class)); | ||
ResponseEntityResultHandler responseEntityResultHandler = this.context.getBean(ResponseEntityResultHandler.class); | ||
assertThat(responseEntityResultHandler.getContentTypeResolver()).isSameAs(this.context.getBean("webFluxContentTypeResolver")); | ||
assertThat(this.context.getBeansOfType(RequestedContentTypeResolver.class)).containsOnlyKeys( | ||
"webFluxContentTypeResolver", "testContentTypeResolver"); | ||
} | ||
|
||
@Test | ||
void responseBodyResultHandlerUsesWebFluxInfrastructureByDefault() { | ||
load(context -> { }); | ||
ResponseBodyResultHandler responseBodyResultHandler = this.context.getBean(ResponseBodyResultHandler.class); | ||
assertThat(responseBodyResultHandler.getAdapterRegistry()).isSameAs(this.context.getBean("webFluxAdapterRegistry")); | ||
assertThat(responseBodyResultHandler.getContentTypeResolver()).isSameAs(this.context.getBean("webFluxContentTypeResolver")); | ||
} | ||
|
||
@Test | ||
void responseBodyResultHandlerWithPrimaryUsesQualifiedReactiveAdapterRegistry() { | ||
load(registerPrimaryBean("testReactiveAdapterRegistry", ReactiveAdapterRegistry.class)); | ||
ResponseBodyResultHandler responseBodyResultHandler = this.context.getBean(ResponseBodyResultHandler.class); | ||
assertThat(responseBodyResultHandler.getAdapterRegistry()).isSameAs(this.context.getBean("webFluxAdapterRegistry")); | ||
assertThat(this.context.getBeansOfType(ReactiveAdapterRegistry.class)).containsOnlyKeys( | ||
"webFluxAdapterRegistry", "testReactiveAdapterRegistry"); | ||
} | ||
|
||
@Test | ||
void responseBodyResultHandlerWithPrimaryUsesQualifiedRequestedContentTypeResolver() { | ||
load(registerPrimaryBean("testContentTypeResolver", RequestedContentTypeResolver.class)); | ||
ResponseBodyResultHandler responseBodyResultHandler = this.context.getBean(ResponseBodyResultHandler.class); | ||
assertThat(responseBodyResultHandler.getContentTypeResolver()).isSameAs(this.context.getBean("webFluxContentTypeResolver")); | ||
assertThat(this.context.getBeansOfType(RequestedContentTypeResolver.class)).containsOnlyKeys( | ||
"webFluxContentTypeResolver", "testContentTypeResolver"); | ||
} | ||
|
||
@Test | ||
void viewResolutionResultHandlerUsesWebFluxInfrastructureByDefault() { | ||
load(context -> { }); | ||
ViewResolutionResultHandler viewResolutionResultHandler = this.context.getBean(ViewResolutionResultHandler.class); | ||
assertThat(viewResolutionResultHandler.getAdapterRegistry()).isSameAs(this.context.getBean("webFluxAdapterRegistry")); | ||
assertThat(viewResolutionResultHandler.getContentTypeResolver()).isSameAs(this.context.getBean("webFluxContentTypeResolver")); | ||
} | ||
|
||
@Test | ||
void viewResolutionResultHandlerWithPrimaryUsesQualifiedReactiveAdapterRegistry() { | ||
load(registerPrimaryBean("testReactiveAdapterRegistry", ReactiveAdapterRegistry.class)); | ||
ViewResolutionResultHandler viewResolutionResultHandler = this.context.getBean(ViewResolutionResultHandler.class); | ||
assertThat(viewResolutionResultHandler.getAdapterRegistry()).isSameAs(this.context.getBean("webFluxAdapterRegistry")); | ||
assertThat(this.context.getBeansOfType(ReactiveAdapterRegistry.class)).containsOnlyKeys( | ||
"webFluxAdapterRegistry", "testReactiveAdapterRegistry"); | ||
} | ||
|
||
@Test | ||
void viewResolutionResultHandlerWithPrimaryUsesQualifiedRequestedContentTypeResolver() { | ||
load(registerPrimaryBean("testContentTypeResolver", RequestedContentTypeResolver.class)); | ||
ViewResolutionResultHandler viewResolutionResultHandler = this.context.getBean(ViewResolutionResultHandler.class); | ||
assertThat(viewResolutionResultHandler.getContentTypeResolver()).isSameAs(this.context.getBean("webFluxContentTypeResolver")); | ||
assertThat(this.context.getBeansOfType(RequestedContentTypeResolver.class)).containsOnlyKeys( | ||
"webFluxContentTypeResolver", "testContentTypeResolver"); | ||
} | ||
|
||
private <T> Consumer<AnnotationConfigApplicationContext> registerPrimaryBean(String beanName, Class<T> type) { | ||
return context -> context.registerBean(beanName, type, () -> mock(type), definition -> definition.setPrimary(true)); | ||
} | ||
|
||
private void load(Consumer<AnnotationConfigApplicationContext> context) { | ||
AnnotationConfigApplicationContext testContext = new AnnotationConfigApplicationContext(); | ||
context.accept(testContext); | ||
testContext.registerBean(DelegatingWebFluxConfiguration.class); | ||
testContext.refresh(); | ||
this.context = testContext; | ||
} | ||
} |
Oops, something went wrong.