Be careful when using @Configuration classes with @EnableWebMvc in Spring Boot

Situation

Recently, we have been faced with a strange issue after adding a configuration class to our Spring Boot application. Unfortunately, the logs didn’t provide any valuable information, becuause we didn’t use the bean in our new configuration class which mention in logs:

1
2
3
Parameter 1 of constructor in com.xyz.spring.starter.web.filter.FilterAutoConfiguration required a bean of type 'org.springframework.web.filter.RequestContextFilter' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.web.filter.RequestContextFilter' in your configuration

And our new configuration class looked like this:

1
2
3
4
5
@Configuration
@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {
// some simple logic
}

After playing with the configuration, we found out that using the @EnableWebMvc annotation breaks our project. So, let’s go deeper into the Spring configuration to understand why this is happening.

What is the @Configuration annotation?

The Spring Framework documentation says: the @Configuration annotation allows us to use annotations for dependency injection. This annotation tels Spring that a class has some methods which create beans that can be used in the application. When the Spring container detects this annotation, it process this class and create these beans.

Just an example of using the @Configuration annotation:

1
2
3
4
5
6
7
8
@Configuration
public class AppConfig {

@Bean
public MyBean myBean() {
// instantiate, configure and return bean ...
}
}

What is the @EnableWebMvc annotation?

The official documentation says:
Adding this annotation to an @Configuration class imports the Spring MVC configuration from WebMvcConfigurationSupport

1
2
3
4
5
@Configuration
@EnableWebMvc
@ComponentScan(basePackageClasses = MyConfiguration.class)
public class MyConfiguration {
}

To customize the imported configuration, implement the interface WebMvcConfigurer and override individual methods, e.g.:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
@EnableWebMvc
@ComponentScan(basePackageClasses = MyConfiguration.class)
public class MyConfiguration implements WebMvcConfigurer {

@Override
public void addFormatters(FormatterRegistry formatterRegistry) {
formatterRegistry.addConverter(new MyConverter());
}

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MyHttpMessageConverter());
}

}

And there is some very interresting point:

Note: only one @Configuration class may have the @EnableWebMvc annotation to import the Spring Web MVC configuration. There can however be multiple @Configuration classes implementing WebMvcConfigurer in order to customize the provided configuration.

In a nutshell

  1. We can have ONLY ONE @Configuration class which annotated with the @EnableWebMvc annotation!

  2. If we need more - we have to implement WebMvcConfigurer.

Keep in mind that this from the Spring Framework Documentation, not from Spring Boot!

What is Spring MVC Auto-configuration?

The Spring Boot Documentation says:

Spring Boot provides auto-configuration for Spring MVC that works well with most applications.

The auto-configuration adds the following features on top of Spring’s defaults:

  • Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.

  • Support for serving static resources, including support for WebJars.

  • Automatic registration of Converter, GenericConverter, and Formatter beans.

  • Support for HttpMessageConverters.

  • Automatic registration of MessageCodesResolver.

  • Static index.html support.

  • Automatic use of a ConfigurableWebBindingInitializer bean.

If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc, or alternatively add your own @Configuration-annotated DelegatingWebMvcConfiguration as described in the Javadoc of @EnableWebMvc.

In a nutshell

  1. You can create your own @Configuration which should implement WebMvcConfigurer. But, do not include the @EnableWebMvc annotation in this class.

  2. You can use @Configuration class with the @EnableWebMvc annotation but you have to take complete control of Spring MVC and customize every aspect of its configuration and be aware that it can cause conflicts with Spring Boot’s auto-configuration and make it harder to configure your application correctly.

What does the Internet say?

Baeldung mention in the section Migrate a Spring Web Application that:

  • When we use spring-boot-starter-web dependency, Spring Boot automatically sets up a DispatcherServlet bean and adds the @EnableWebMvc annotation to the main Application class.

  • If we use the @EnableWebMvc annotation in a @Configuration class, it will disable Spring Boot’s auto-configuration for MVC.

Some guy from Quora:

Spring Boot doesn’t mix well with the standard Spring MVC @EnableWebMvc. What happens when you add the annotation is that spring boot auto-configuration is disabled.

Don’t use @EnableWebMvc in spring boot, just include spring-web as a maven/gradle dependency and it will be auto-configured.

Comments from Stackoverflow

Normally you would add @EnableWebMvc for a Spring MVC app, but Spring Boot adds it automatically when it sees spring-webmvc on the classpath. This flags the application as a web application and activates key behaviors such as setting up a DispatcherServlet.

It is important to remember that 3rd point. Do not annotate any @Configuration classes with @EnableWebMvc. Otherwise, Spring MVC will load and use its own serialization/deserialization configuration, ignoring your Spring Boot config.

What you can do?

Avoid using @EnableWebMvc in spring boot, just use spring-boot-starter-web dependency and it will be auto-configured. If you need to use @EnableWebMvc annotation in a @Configuration be aware of the consequences.

References:

Share