NOTE

Spring MVC Auto-Configuration Principle

1. Auto-configuration classes pre-imported by spring-boot-starter-web. 2. How custom configuration takes effect. 3. Why @EnableWebMvc disables Spring Boot MVC auto-configuration. 4. Reference.

JavaCreated Updated 1 min readhistorical

This is a historical learning note and may contain outdated or incomplete understanding.

1. Auto-Configuration Classes Pre-Imported by spring-boot-starter-web

  • @WebMvcAutoconfiguration

WebMvcAutoConfigurationAdapter is the class through which Spring Boot enables Web MVC auto-configuration, as shown below.

@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
        WebMvcConfigurerAdapter.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
        ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {


        @Configuration
        @Import(EnableWebMvcConfiguration.class)// The key is this EnableWebMvcConfiguration
        @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
        // This class extends WebMvcConfigurerAdapter
        public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter {
            //...
        }
}
  • It contains a static inner configuration class, WebMvcAutoConfigurationAdapter.

It extends WebMvcConfigurerAdapter: by overriding some methods, MVC can be configured.

  • At the same time, it also imports a class named EnableWebMvcConfiguration.

This class is shown below. It extends DelegatingWebMvcConfiguration [the main focus of the analysis].

@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration

2. Principle Behind Custom Configuration Taking Effect

2.1. Inject WebMvcConfigurerComposite and List

  • DelegatingWebMvcConfiguration uses:
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

    // The key is this
    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();


    @Autowired(required = false)// Indicates that method parameters are obtained from the container
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }

    // Take the method below as an example
    @Override
    protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        this.configurers.configureContentNegotiation(configurer);
    }
    }

2.2. Let WebMvcConfigurerComposite Call WebMvcConfigurer Methods to Customize Spring MVC

  • The setConfigurers method in this class uses the @Autowired annotation, so all WebMvcConfigurer instances in the container are injected into the parameter List<WebMvcConfigurer> configurers.
  • At the same time, this class also has a WebMvcConfigurerComposite field, which is specifically used to manage all WebMvcConfigurer instances.

Take the configureContentNegotiation method as an example. It calls WebMvcConfigurerComposite.configureContentNegotiation. This method traverses all WebMvcConfigurer instances and invokes their configureContentNegotiation methods, as shown below.

class WebMvcConfigurerComposite implements WebMvcConfigurer {
    private final List<WebMvcConfigurer> delegates = new ArrayList<WebMvcConfigurer>();
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        // Call every WebMvcConfigurer in the container, including ours and Spring Boot's
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.configureContentNegotiation(configurer);
        }
    }
}

3. Why Using @EnableWebMvc Causes Spring Boot MVC Auto-Configuration to Stop Working

First look at the class imported by EnableWebMvc, DelegatingWebMvcConfiguration.

@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

This DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport, which is equivalent to importing WebMvcConfigurationSupport.

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport

Then look at the MVC auto-configuration class WebMvcAutoConfiguration.

@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
        WebMvcConfigurerAdapter.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)// This class only takes effect when WebMvcConfigurationSupport is missing
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
        ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration

3.1. Conditional Annotation Causes the Preconfiguration to Stop Working

  • In fact, the WebMvcAutoConfiguration class that spring-boot-starter-web uses to preconfigure MVC requires:

@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) // This class only takes effect when WebMvcConfigurationSupport is missing

  • If we configure @EnableWebMvc, it imports DelegatingWebMvcConfiguration, which extends WebMvcConfigurationSupport.
  • The two conflict, so auto-configuration stops working.

3.2. How to Add Custom MVC Configuration

@Configuration
// Do not use @EnableWebMvc, otherwise MVC is fully taken over -- that is, Spring Boot auto-configuration is no longer used
public class MyWebMvcConfig extends WebMvcConfigurerAdapter// Extends WebMvcConfigurerAdapter and implements the WebMvcConfigurer interface
{
    // Override the corresponding methods as needed. This allows both extension and use of the default implementation
}

4. Reference

Discussion

Sign in with GitHub to comment. Discussions are stored as GitHub Issues.View on GitHub