Add WebMvcRegistrations for custom MVC components
Add `WebMvcRegistrations` which can be used to provide custom instances of `RequestMappingHandlerMapping`, `RequestMappingHandlerAdapter` and `ExceptionHandlerExceptionResolver`. Those instances are then used and processed by the Boot MVC configuration. Prior to this commit, developers could provide their custom instances of MVC infrstructure components such as `RequestMappingHandlerMapping` and `RequestMappingHandlerAdapter` only by using advanced configuration strategies. Those advanced configurations involved subclassing `WebMvcConfigurationSupport` which effectively turns off MVC auto-configuration in Boot. Fixes gh-5004 Closes gh-6100
This commit is contained in:
parent
5250fb127a
commit
6dc0ecb182
|
|
@ -83,6 +83,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupp
|
|||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.i18n.FixedLocaleResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.springframework.web.servlet.resource.AppCacheManifestTransformer;
|
||||
|
|
@ -342,10 +343,14 @@ public class WebMvcAutoConfiguration {
|
|||
|
||||
private final ListableBeanFactory beanFactory;
|
||||
|
||||
private final WebMvcRegistrations mvcRegistrations;
|
||||
|
||||
public EnableWebMvcConfiguration(
|
||||
ObjectProvider<WebMvcProperties> mvcPropertiesProvider,
|
||||
ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider,
|
||||
ListableBeanFactory beanFactory) {
|
||||
this.mvcProperties = mvcPropertiesProvider.getIfAvailable();
|
||||
this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
|
|
@ -358,6 +363,15 @@ public class WebMvcAutoConfiguration {
|
|||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
|
||||
if (this.mvcRegistrations != null
|
||||
&& this.mvcRegistrations.getRequestMappingHandlerAdapter() != null) {
|
||||
return this.mvcRegistrations.getRequestMappingHandlerAdapter();
|
||||
}
|
||||
return super.createRequestMappingHandlerAdapter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
@Override
|
||||
|
|
@ -366,6 +380,15 @@ public class WebMvcAutoConfiguration {
|
|||
return super.requestMappingHandlerMapping();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
|
||||
if (this.mvcRegistrations != null
|
||||
&& this.mvcRegistrations.getRequestMappingHandlerMapping() != null) {
|
||||
return this.mvcRegistrations.getRequestMappingHandlerMapping();
|
||||
}
|
||||
return super.createRequestMappingHandlerMapping();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
|
||||
try {
|
||||
|
|
@ -376,6 +399,14 @@ public class WebMvcAutoConfiguration {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExceptionHandlerExceptionResolver createExceptionHandlerExceptionResolver() {
|
||||
if (this.mvcRegistrations != null && this.mvcRegistrations
|
||||
.getExceptionHandlerExceptionResolver() != null) {
|
||||
return this.mvcRegistrations.getExceptionHandlerExceptionResolver();
|
||||
}
|
||||
return super.createExceptionHandlerExceptionResolver();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2012-2016 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
|
||||
*
|
||||
* http://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.boot.autoconfigure.web;
|
||||
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
|
||||
/**
|
||||
* Interface to register key components of the {@link WebMvcConfigurationSupport} in place
|
||||
* of the default ones provided by Spring MVC.
|
||||
* <p>
|
||||
* All custom instances are later processed by Boot and Spring MVC configurations. A
|
||||
* single instance of this component should be registered, otherwise making it impossible
|
||||
* to choose from redundant MVC components.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 1.4.0
|
||||
* @see org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.EnableWebMvcConfiguration
|
||||
*/
|
||||
public interface WebMvcRegistrations {
|
||||
|
||||
/**
|
||||
* Return the custom {@link RequestMappingHandlerMapping} that should be used and
|
||||
* processed by the MVC configuration.
|
||||
* @return the custom {@link RequestMappingHandlerMapping} instance
|
||||
*/
|
||||
RequestMappingHandlerMapping getRequestMappingHandlerMapping();
|
||||
|
||||
/**
|
||||
* Return the custom {@link RequestMappingHandlerAdapter} that should be used and
|
||||
* processed by the MVC configuration.
|
||||
* @return the custom {@link RequestMappingHandlerAdapter} instance
|
||||
*/
|
||||
RequestMappingHandlerAdapter getRequestMappingHandlerAdapter();
|
||||
|
||||
/**
|
||||
* Return the custom {@link ExceptionHandlerExceptionResolver} that should be used and
|
||||
* processed by the MVC configuration.
|
||||
* @return the custom {@link ExceptionHandlerExceptionResolver} instance
|
||||
*/
|
||||
ExceptionHandlerExceptionResolver getExceptionHandlerExceptionResolver();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright 2012-2016 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
|
||||
*
|
||||
* http://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.boot.autoconfigure.web;
|
||||
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
|
||||
/**
|
||||
* An implementation of {@link WebMvcRegistrations} with empty methods allowing
|
||||
* sub-classes to override only the methods they're interested in.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class WebMvcRegistrationsAdapter implements WebMvcRegistrations {
|
||||
|
||||
@Override
|
||||
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExceptionHandlerExceptionResolver getExceptionHandlerExceptionResolver() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -45,6 +45,7 @@ import org.springframework.boot.test.util.EnvironmentTestUtils;
|
|||
import org.springframework.boot.web.filter.OrderedHttpPutFormContentFilter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
|
|
@ -65,6 +66,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
|
|||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.i18n.FixedLocaleResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.springframework.web.servlet.resource.AppCacheManifestTransformer;
|
||||
import org.springframework.web.servlet.resource.CachingResourceResolver;
|
||||
import org.springframework.web.servlet.resource.CachingResourceTransformer;
|
||||
|
|
@ -468,6 +470,29 @@ public class WebMvcAutoConfigurationTests {
|
|||
.isInstanceOf(CustomWebBindingInitializer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customRequestMappingHandlerMapping() {
|
||||
load(CustomRequestMappingHandlerMapping.class);
|
||||
assertThat(this.context.getBean(RequestMappingHandlerMapping.class))
|
||||
.isInstanceOf(MyRequestMappingHandlerMapping.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customRequestMappingHandlerAdapter() {
|
||||
load(CustomRequestMappingHandlerAdapter.class);
|
||||
assertThat(this.context.getBean(RequestMappingHandlerAdapter.class))
|
||||
.isInstanceOf(MyRequestMappingHandlerAdapter.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleWebMvcRegistrations() {
|
||||
load(MultipleWebMvcRegistrations.class);
|
||||
assertThat(this.context.getBean(RequestMappingHandlerMapping.class))
|
||||
.isNotInstanceOf(MyRequestMappingHandlerMapping.class);
|
||||
assertThat(this.context.getBean(RequestMappingHandlerAdapter.class))
|
||||
.isNotInstanceOf(MyRequestMappingHandlerAdapter.class);
|
||||
}
|
||||
|
||||
private void load(Class<?> config, String... environment) {
|
||||
this.context = new AnnotationConfigEmbeddedWebApplicationContext();
|
||||
EnvironmentTestUtils.addEnvironment(this.context, environment);
|
||||
|
|
@ -594,4 +619,53 @@ public class WebMvcAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomRequestMappingHandlerMapping {
|
||||
|
||||
@Bean
|
||||
public WebMvcRegistrationsAdapter webMvcRegistrationsHandlerMapping() {
|
||||
return new WebMvcRegistrationsAdapter() {
|
||||
|
||||
@Override
|
||||
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
|
||||
return new MyRequestMappingHandlerMapping();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyRequestMappingHandlerMapping
|
||||
extends RequestMappingHandlerMapping {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomRequestMappingHandlerAdapter {
|
||||
|
||||
@Bean
|
||||
public WebMvcRegistrationsAdapter webMvcRegistrationsHandlerAdapter() {
|
||||
return new WebMvcRegistrationsAdapter() {
|
||||
|
||||
@Override
|
||||
public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
|
||||
return new MyRequestMappingHandlerAdapter();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyRequestMappingHandlerAdapter
|
||||
extends RequestMappingHandlerAdapter {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import({ CustomRequestMappingHandlerMapping.class,
|
||||
CustomRequestMappingHandlerAdapter.class })
|
||||
static class MultipleWebMvcRegistrations {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1457,12 +1457,16 @@ The auto-configuration adds the following features on top of Spring's defaults:
|
|||
* Custom `Favicon` support.
|
||||
* Automatic use of a `ConfigurableWebBindingInitializer` bean (see below).
|
||||
|
||||
If you want to take complete control of Spring MVC, you can add your own `@Configuration`
|
||||
annotated with `@EnableWebMvc`. If you want to keep Spring Boot MVC features, and
|
||||
If you want to keep Spring Boot MVC features, and
|
||||
you just want to add additional {spring-reference}#mvc[MVC configuration] (interceptors,
|
||||
formatters, view controllers etc.) you can add your own `@Bean` of type
|
||||
`WebMvcConfigurerAdapter`, but *without* `@EnableWebMvc`.
|
||||
`WebMvcConfigurerAdapter`, but *without* `@EnableWebMvc`. If you wish to provide custom
|
||||
instances of `RequestMappingHandlerMapping`, `RequestMappingHandlerAdapter` or
|
||||
`ExceptionHandlerExceptionResolver` you can declare a `WebMvcRegistrationsAdapter`
|
||||
instance providing such components.
|
||||
|
||||
If you want to take complete control of Spring MVC, you can add your own `@Configuration`
|
||||
annotated with `@EnableWebMvc`.
|
||||
|
||||
|
||||
[[boot-features-spring-mvc-message-converters]]
|
||||
|
|
|
|||
Loading…
Reference in New Issue