【WEEK10】 【DAY1】MVC Auto-Configuration Principles【English Version】

2024.4.29 Monday

9. MVC Auto-Configuration Principles

9.1. Official Documentation Reading

9.1.1. Before we start coding the project, we need to know what Spring Boot has configured for our Spring MVC, including how to extend and customize it.
9.1.2. Only by understanding these will we be more proficient in using it later. Approach one: Source code analysis, Approach two: Official documentation!
9.1.3. Link: https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration

Spring MVC Auto-configuration

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 (covered later in this document).

Automatic registration of MessageCodesResolver (covered later in this document).

Static index.html support.

Custom Favicon support (covered later in this document).

Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).

If you want to keep Spring Boot MVC features and you want to add additional MVC configuration 
(interceptors, formatters, view controllers, and other features), you can add your own 
@Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide 
custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or 
ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.

9.1.4. Let’s carefully compare and see how it’s implemented. It tells us that Spring Boot has automatically configured Spring MVC for us, so what exactly has it automatically configured?

9.2. ContentNegotiatingViewResolver - Content Negotiation View Resolver

9.2.1. Automatic Configuration of ViewResolver, which is the view resolver we previously learned about in Spring MVC;

9.2.2. That is, based on the method’s return value, get the view object (View), and then let the view object decide how to render (forward, redirect).

9.2.3. Let’s look at the source code here: We find WebMvcAutoConfiguration, then search for ContentNegotiatingViewResolver. Find the following method!

WebMvcAutoConfiguration.java(line305)
@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
   ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
   resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
   // ContentNegotiatingViewResolver uses all the other view resolvers to locate
   // a view so it should have a high precedence
   resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
   return resolver;
}

9.2.4. Create MyMvcConfig.java

Image Description

package com.P14.config;
// This is a custom MVC configuration
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.Locale;

// Want to extend Spring MVC using this class
// Official documentation: 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.
@Configuration
// Upon inspection, WebMvcConfigurer.java is an interface, so here we can implement the interface using implements WebMvcConfigurer
// Extend Spring MVC: Use DispatcherServlet
public class MyMvcConfig implements WebMvcConfigurer {
    // ViewResolver implements the view resolver interface class, so we can consider it as a view resolver

    // Customized our own view resolver
    @Bean
    public ViewResolver myViewResolver() {
        return new MyViewResolver();
    }

    public static class MyViewResolver implements ViewResolver {
        @Override
        public View resolveViewName(String s, Locale locale) throws Exception {
            return null;
        }
    }
}

9.2.5. Open ContentNegotiatingViewResolver.java

As seen,

public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
      implements ViewResolver, Ordered, InitializingBean

Represents the implementation of the view resolver structure. Clicking on ViewResolver leads to AbstractCachingViewResolver.java, a class that implements the view resolver interface -> can be referred to as an implementation of the view resolver

9.2.6. Default View Resolver

ContentNegotiatingViewResolver.java (line222) resolveViewName

@Nullable // Annotation Note: @Nullable indicates that the parameter can be null
public View resolveViewName(String viewName, Locale locale) throws Exception {
   RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
   Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
   List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
   if (requestedMediaTypes != null) {
	// Gets the candidate view object
      List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
	// Select a view object that best fits, and return that object
      View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);
      if (bestView != null) {
         return bestView;
      }
   }

   String mediaTypeInfo = logger.isDebugEnabled() && requestedMediaTypes != null ?
         " given " + requestedMediaTypes.toString() : "";

   if (this.useNotAcceptableStatusCode) {
      if (logger.isDebugEnabled()) {
         logger.debug("Using 406 NOT_ACCEPTABLE" + mediaTypeInfo);
      }
      return NOT_ACCEPTABLE_VIEW;
   }
   else {
      logger.debug("View remains unresolved" + mediaTypeInfo);
      return null;
   }
}

Click getCandidateViews (line228) to access the method (line333) to get the candidate view, go through all view parsers, wrap it into an object, and finally return the view.

private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes)
      throws Exception {

   List<View> candidateViews = new ArrayList<>();
   if (this.viewResolvers != null) {
      Assert.state(this.contentNegotiationManager != null, "No ContentNegotiationManager set");
      for (ViewResolver viewResolver : this.viewResolvers) {
         View view = viewResolver.resolveViewName(viewName, locale);
         if (view != null) {
            candidateViews.add(view);
         }
         for (MediaType requestedMediaType : requestedMediaTypes) {
            List<String> extensions = this.contentNegotiationManager.resolveFileExtensions(requestedMediaType);
            for (String extension : extensions) {
               String viewNameWithExtension = viewName + '.' + extension;
               view = viewResolver.resolveViewName(viewNameWithExtension, locale);
               if (view != null) {
                  candidateViews.add(view);
               }
            }
         }
      }
   }
   if (!CollectionUtils.isEmpty(this.defaultViews)) {
      candidateViews.addAll(this.defaultViews);
   }
   return candidateViews;
}

So come to the conclusion: ContentNegotiatingViewResolver the parser is used to combine all the views of the parser.

9.2.7. How to Check if Our Custom View Resolver Works

Find DispatcherServlet (I couldn’t find it here, probably due to version issues)
9.2.7.1. Set a breakpoint in the doDispatch method of DispatcherServlet for debugging, because all requests will go through this method.
Image Description
9.2.7.2. Start the project, then randomly access a page and check the Debug information.
Find this (which is DispatcherServlet)
Image Description
Find the view resolver, and we see our own defined one here.
Image Description
So, if we want to use our customizations, we just need to add this component to the container! Spring Boot will take care of the rest!

9.3. Converters and Formatters

9.3.1. Find Formatter Converters in WebMvcAutoConfiguration.java:

Line522

@Bean
@Override
public FormattingConversionService mvcConversionService() {
   Format format = this.mvcProperties.getFormat();
// Get a format from the configuration file
   WebConversionService conversionService = new WebConversionService(
         new DateTimeFormatters().dateFormat(format.getDate())
            .timeFormat(format.getTime())
            .dateTimeFormat(format.getDateTime()));
   addFormatters(conversionService);
   return conversionService;
}

Click on getFormat at line525, jump to line123,

public Format getFormat() {
   return this.format;
}

Click on format, jump to line48 of WebMvcProperties.java,

private final Format format = new Format();

Then click on Format to jump to line456 to see the default format:

public static class Format {

   /**
    * Date format to use, for example 'dd/MM/yyyy'.
    */
   private String date;

   /**
    * Time format to use, for example 'HH:mm:ss'.
    */
   private String time;

   /**
    * Date-time format to use, for example 'yyyy-MM-dd HH:mm:ss'.
    */
   private String dateTime;

   public String getDate() {
      return this.date;
   }

   public void setDate(String date) {
      this.date = date;
   }

   public String getTime() {
      return this.time;
   }

   public void setTime(String time) {
      this.time = time;
   }

   public String getDateTime() {
      return this.dateTime;
   }

   public void setDateTime(String dateTime) {
      this.dateTime = dateTime;
   }

}

Image Description
You can also modify the configuration yourself:
Image Description

9.4. Modify Spring Boot’s Default Configuration

  • With so many auto-configurations, the principle is the same. Through the analysis of WebMVC’s auto-configuration principle, we need to learn a way of learning, exploring through source code, and drawing conclusions; this conclusion must belong to ourselves, and it’s universally applicable.
  • Spring Boot’s underlying implementation relies heavily on these design details, so it’s necessary to read the source code more often! Draw conclusions;
  • When Spring Boot auto-configures many components, it first checks if there are any user-configured ones in the container (if users configure @Bean), if so, it uses the user-configured ones, if not, it uses the auto-configured ones;
  • If some components can have multiple instances, such as our view resolver, it combines the user-configured and its own default ones!

9.4.1. Modify MyMvcConfig.java

package com.P14.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

//If you want to extend SpringMVC, it is recommended to do it like this according to the official documentation
@Configuration
//@EnableWebMvc   //Imported a class, DelegatingWebMvcConfiguration, to get all webMvcConfigs from the container
public class MyMvcConfig implements WebMvcConfigurer {
    //Shortcut key: Alt+Insert
    //View redirection
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
//        WebMvcConfigurer.super.addViewControllers(registry);
        registry.addViewController("/P114").setViewName("test");
    }
}

Restart and access
http://localhost:8080/P114
Image Description

9.4.2. Summary

@EnableWebMvc imports the WebMvcConfigurationSupport component;
The imported WebMvcConfigurationSupport is only the most basic functionality of SpringMVC!
In Spring Boot, there are many xxxxconfigure components to help us extend configurations. Whenever you see this, pay attention!

  • 47
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值