preface

A few days ago, during project practice, a strange situation occurred where the Spring Boot parameter binding failed. It was just because a colleague removed the @requestParam annotation from the parameter. As we all know, if the parameter name is the same as the Controller method name, there is no need for @requestParam annotation, Spring Boot will automatically bind the parameter for us. But why did the automatic binding mechanism fail? In this article, you’ll get to the bottom of the puzzle, and along the way, you’ll get to the bottom of how to customize WebMvc in a Spring Boot project, as well as the many pitfalls involved.

SpringBoot custom WebMvc

Spring Boot provides the default configuration for Spring MVC, including a view parser, static resource handling, type converters and formatters, HTTP message converters, static home page support, etc. It is easy to use. However, in practice, customized Web MVC configuration is inevitable. Spring Boot successively provided WebMvcConfigurerAdapter, WebMvcConfigurationSupport, WebMvcConfigurer, @ EnableWebMvc form to realize the Web MVC custom configurations. Let’s learn one by one.

Deprecated WebMvcConfigurerAdapter

In Spring Boot1.0+, you can use the WebMvcConfigurerAdapter to extend the functionality of Spring MVC. WebMvcConfigurerAdapter is an abstract implementation class for WebMvcConfigurer. All method implementations are empty and the subclasses implement whatever functionality they need.

After Spring 5.0, in Spring Boot2.0, the JDK was implemented based on Java8, where the interface methods could be defined as default. A method subclass defined as default in an interface may not be implemented. The WebMvcConfigurer interface uses Java8 features, so the WebMvcConfigurerAdapter is useless.

As a result, the WebMvcConfigurerAdapter class is deprecated in Spring Boot2.0.

@Deprecated
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {
Copy the code

If you look at the WebMvcConfigurerAdapter implementation, you will see that it simply implements all the methods of the interface as an empty method. Java8’s default feature completely overrides this capability. If you’re developing based on Spring Boot 2.x and implement functionality through extends WebMvcConfigurerAdapter, you can replace it with implements WebMvcConfigurer. We’ll talk about the implementation later.

Overwrite WebMvcConfigurationSupport

WebMvcConfigurerAdapter were abandoned, so we still can be done through inheritance WebMvcConfigurationSupport Spring MVC.

public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {... }Copy the code

This class is special in that it implements the ApplicationContextAware and ServletContextAware interfaces, provides some default implementations, and provides many @bean methods, but does not provide the @Configureation annotation. Therefore, these @beans do not take effect, so we need to inherit the class and provide the @Configureation annotation on the supplied class to take effect.

WebMvcConfigurationSupport not only defines the Bean, also provide a large amount of the add and config at the beginning of the method. Comparing WebMvcConfigurer method definition, will found almost WebMvcConfigurer some in WebMvcConfigurationSupport has. It is important to note that in some way WebMvcConfigurationSupport did not achieve specific function.

AddInterceptors and addViewControllers are common examples:

/**
* Override this method to add Spring MVC interceptors for
* pre- and post-processing of controller invocation.
* @see InterceptorRegistry
 */
protected void addInterceptors(InterceptorRegistry registry) {
}

/**
 * Override this method to add view controllers.
 * @see ViewControllerRegistry
*/
protected void addViewControllers(ViewControllerRegistry registry) {
}
Copy the code

Inheritance WebMvcConfigurationSupport after, can use the method to add custom interceptors, view the parser functions, such as the sample is as follows:

@Configuration public class WebMvcConfig extends WebMvcConfigurationSupport { @Override protected void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("login"); registry.addViewController("/login.html").setViewName("login"); }}Copy the code

At this point, you think everything is ok and you can happily write business logic? That’s when the problem starts. When through inheritance WebMvcConfigurationSupport implemented MVC configuration, to Spring the Boot to replace the default configuration of MVC. Once replaced, Spring Boot’s default convention over configuration features may fail, such as static resource access failure, unsuccessful return of data, and, of course, parameter binding failure mentioned earlier.

Why, then, inheritance WebMvcConfigurationSupport will replace the Spring Boot default MVC configuration? Let’s take a look at Spring Boot’s implementation of automatic assembly of WEB MVC related components:

@Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class}) @ConditionalOnMissingBean({WebMvcConfigurationSupport.class}) @AutoConfigureOrder(-2147483638) @AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class}) public class WebMvcAutoConfiguration {... }Copy the code

Spring Boot uses the WebMvcAutoConfiguration configuration class to set the default parameters (conventions) of MVC, but WebMvcAutoConfiguration takes effect under certain conditions. @ ConditionalOnMissingBean ({WebMvcConfigurationSupport. Class}) specifies, when are not present in the Spring container type for WebMvcConfigurationSupport bean, The default configuration is performed. Must be of a custom WebMvcConfigurationSupport, so will lead to WebMvcAutoConfiguration cannot be instantiated, and internal initialization configuration will all cannot be instantiated.

In this case, the relevant configuration needs to be implemented by itself, unless there is excellent control of the code, or a lot of special customization, this form is considered. Otherwise, a list of conventions will cease to exist, and some puzzling problems may occur.

The ultimate killer WebMvcConfigurer

Given the two unworkable or flawed ways mentioned above, the final solution, of course, is to implement the WebMvcConfigurer interface.

But after learning the way WebMvcConfigurationSupport, whether or not you, will also appear to cover? Obviously, there is no restriction on the existence of WebMvcConfigurer’s Bean in the WebMVC auto-configuration class. Therefore, the default configuration does not become invalid because the interface is implemented. In addition, Spring Boot supports multiple WebMvcConfigurer implementation classes.

As mentioned above, Spring boot2. x is based on Java8. One major change in Java8 is that the default method can be used in the interface, which is not mandatory. The webMVCConfigerAdapter class implements the WebMvcConfigurer interface. The webMVCConfigerAdapter class implements the WebMvcConfigurer interface. Usage is the same as inheritance adaptation classes. Such as:

@Configuration public class MyMVCConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/user").setViewName("success"); }}Copy the code

If you are not sure, you can try it out and see that Spring Boot’s automatic configuration does not fail to implement the WebMvcConfigurer interface. In general, this form is also recommended for customizing Web MVC.

Let’s take a closer look at how implementing the WebMvcConfigurer interface keeps both the custom and default configurations in effect. Back to the front of the automatic configuration class WebMvcAutoConfiguration, within the class will be initialized an inner class EnableWebMvcConfiguration:

@Configuration public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {... }Copy the code

The inner class inherits DelegatingWebMvcConfiguration class, DelegatingWebMvcConfiguration is a proxy class to configure Spring MVC, It combines default and user configurations to define the configuration that the Spring MVC runtime will ultimately use. DelegatingWebMvcConfiguration inherited from WebMvcConfigurationSupport and WebMvcConfigurationSupport provides the default configuration for Spring MVC.

@Configuration public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { @Autowired(required = false) public void setConfigurers(List<WebMvcConfigurer> configurers) { if (! CollectionUtils.isEmpty(configurers)) { this.configurers.addWebMvcConfigurers(configurers); }} / /... }Copy the code

DelegatingWebMvcConfiguration itself USES annotations @ the Configuration, so it is a Configuration class that will be as a singleton bean registered to the container and corresponding rely on injected by the container. WebMvcConfigurer into DelegatingWebMvcConfiguration by setConfigurers (List < WebMvcConfigurer > configurers) method to implement. This method uses the annotation @AutoWired (Required = false), which collects all the configurations from the WebMvcConfigurer implementation classes and combines them together to form a super configuration. Therefore, any class that implements the WebMvcConfigurer interface is injected through the setConfigurers method, and multiple Instances of WebMvcConfigurer are stored as a List.

Therefore, directly implementing the WebMvcConfigurer interface does not overwrite the default configuration, but can also add customized configurations. So is it OK to use the form that implements the WebMvcConfigurer interface? If you look around the web for some sample code, there will be some potholes waiting for you, so keep reading.

@enableWebMVC usage scenario

In many code examples, you will also see that it implements not only the WebMvcConfigurer interface, but also the @enableWebMVC annotation on the implementation class, which you need to pay attention to. The @enableWebMVC annotation causes a new round of default WebMVC configuration invalidation.

Earlier official versions had instructions like the following: If you want to keep the WebMVC features provided by Spring Boot by default and add additional functionality, you can simply inherit the WebMvcConfigurerAdapter or implement the WebMvcConfigurer interface. It can then be instantiated using the @Configuration annotation on the implementation class without using @enableWebMVC. If you want full control of Spring MVC, you can add the @enableWebMVC annotation to the implementation class.

That is, the @enableWebMVC annotation does not have to be configured and is only used if the default configuration is to be completely overridden. It is also specified in the source code comment that only one class can use the @EnableWebMVC annotation throughout the project, unlike the WebMvcConfigurer interface, which can have multiple implementation classes.

So, once the @enableWebMVC annotation is used in the code, it means that the Spring MVC auto-configuration is disabled and everything needs to be auto-configured. The following is an example:

@Configuration @EnableWebMvc public class WebDispatcherServletConfigure implements WebMvcConfigurer { @Bean public ViewResolver viewResolver() { InternalResourceViewResolver resourceViewResolver = new InternalResourceViewResolver(); resourceViewResolver.setViewClass(JstlView.class); resourceViewResolver.setPrefix("/WEB-INF/jsp/"); resourceViewResolver.setSuffix(".jsp"); return resourceViewResolver; }}Copy the code

So why does the @enableWebMVC annotation cause the default to take effect? Let’s take a look at its source code:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
Copy the code

On the annotations EnableWebMvc by @ Import Import DelegatingWebMvcConfiguration class. Does this class look familiar? Right, that is, the inherited WebMvcConfigurationSupport class we talked about above. That is to say, once use @ EnableWebMvc comment, will import a WebMvcConfigurationSupport, which produces a Bean. At this point, going back to the previous conclusion, WebMvcAutoConfiguration finds that such a Bean already exists and will not be instantiated.

Summary is WebMvcConfigurationSupport has three kinds of realization forms: first, the realization of the user-defined WebMvcConfigurationSupport class, at this point will cause WebMvcAutoConfiguration not instantiate; Second, using the @ EnableWebMvc annotations, is equal to the expanded the WebMvcConfigurationSupport, but there is no override any way, at this time also causes WebMvcAutoConfiguration not instantiate; Third, using the default configuration, WebMvcAutoConfiguration corresponding WebMvcConfigurationSupport implementation class to instantiate.

Therefore, we usually implement the WebMvcConfigurer interface and leave Spring Boot with the default configuration.

WebMvc common configuration

Having seen the differences and pitfalls of the various configuration forms above, after choosing the appropriate WebMVC configuration form, we will take a look at what configuration is generally available, focusing on the implementation class of the interface.

Static Resource Configuration

Override addResourceHandlers to configure path access and so on, Spring Boot in mapping the classpath to use ResourceHttpRequestHandler by default/static/public, resources, and/METAINF/resources directory or ServletContext static files in the root directory of the direct mapping For/with / * * * *.

Public void addResourceHandlers(ResourceHandlerRegistry registry) {// Static resource path CSS,js,img, etc registry.addResourceHandler("/statics/**").addResourceLocations("classpath:/statics/"); / / view registry. AddResourceHandler ("/templates / * * "). AddResourceLocations (" classpath: / templates/"); //mapper.xml registry.addResourceHandler("/mapper/**").addResourceLocations("classpath:/mapper/"); super.addResourceHandlers(registry); }Copy the code

Interceptor configuration

Rewrite the addInterceptors() method to configure the interceptor (which implements the HandlerInterceptor interface), etc. The addInterceptors method implemented here corresponds to the < MVC: Interceptors > configuration in the XML file.

@Autowired private MyInteceptor myInteceptor; @override public void addInterceptors(InterceptorRegistry registry) { AddInterceptor (myInteceptor) // addInterceptor. AddPathPatterns ("/**") // addInterceptor path ExcludePathPatterns (/ / exclude intercept path "/ statics / * * / *. *",); super.addInterceptors(registry); }Copy the code

Cross-domain configuration

Rewrite the addCorsMappings method to implement configuring CORS cross-domain restrictions, etc.

@override public void addCorsMappings(CorsRegistry registry) {registry. AddMapping ("/**")// Configure the connection routes that are allowed across domains AllowedOrigins (" * "), / / configuration is allowed to access the resources of cross-domain request domain. The allowedMethods (" PUT, POST, GET, DELETE, the OPTIONS ") / / configuration allows access to the cross-domain resources server request method, such as: POST, GET, PUT, DELETE, etc. AllowedHeaders ("*"); // Configure to allow header access, such as x-token super.addCorsmappings (Registry); }Copy the code

View Controller Configuration

Rewrite the addViewControllers method to configure view mapping, etc.

@Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("/home"); // The default view jumps registry. SetOrder (order.highest_precedence); super.addViewControllers(registry); }Copy the code

Message converter configuration

Rewrite configureMessageConverters method to transform for news. MessageConverter is used to convert the returned RESULT of HTTP request in fastjon, application/ JSON format. Charset =UTF-8 for conversion.

@Override public void configureMessageConverters(List<HttpMessageConverter<? >> converters) { StringHttpMessageConverter converter = new StringHttpMessageConverter(Charset.forName("UTF-8")); converters.add(converter); }Copy the code

Data formatter configuration

Override the addFormatters method to add a data formatter, such as converting a string to a date type. The DateFormatter class does the automatic conversion.

Formatters and converters are used to convert Date formats. By default, Number and Date formatters are registered, supporting @numberFormat and @dateTimeFormat. You need to customize formatters and converters to implement the addFormatters method.

@Override
public void addFormatters(FormatterRegistry registry) {
    super.addFormatters(registry);
    registry.addFormatter(new DateFormatter("yyyy-MM-dd"));
}
Copy the code

View resolver configuration

Override the configureViewResolvers() method to configure the view resolvers, primarily to configure the prefixes and suffixes of the view.

@Override public void configureViewResolvers(ViewResolverRegistry registry) { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix(""); viewResolver.setSuffix(".html"); viewResolver.setCache(false); viewResolver.setContentType("text/html; charset=UTF-8"); viewResolver.setOrder(0); registry.viewResolver(viewResolver); super.configureViewResolvers(registry); }Copy the code

Simplified versions are also provided for adoption, such as:

@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
    registry.jsp("/", ".jsp");
}
Copy the code

summary

Through the learning of this article, you must have a detailed understanding of the default configuration of Spring Boot, how to customize the configuration, and the implementation of specific methods. The most critical is through different forms of expression, constantly trace to the bottom implementation, ultimately achieve from the bottom principle to the upper application through the effect. Therefore, in the process of practice, we do not ignore any small exceptions or bugs, in-depth additional to open a new world.

Custom SpringBoot default MVC configuration?


Program new horizon

\

The public account “program new vision”, a platform for simultaneous improvement of soft power and hard technology, provides massive information

\