天天看點

MVC配置原理-源碼

目錄

  • 舉例
  • 修改SpringBoot的預設配置
  • 全面接管SpringMVC
  • 參考連結

在進行項目編寫前,我們還需要知道一個東西,就是SpringBoot對我們的SpringMVC還做了哪些配置,包括如何擴充,如何定制。

途徑一:源碼分析

途徑二:官方文檔

ContentNegotiatingViewResolver 内容協商視圖解析器

自動配置了ViewResolver,就是我們之前學習的SpringMVC的視圖解析器;

即根據方法的傳回值取得視圖對象(View),然後由視圖對象決定如何渲染(轉發,重定向)。

我們去看看這裡的源碼:我們找到 WebMvcAutoConfiguration , 然後搜尋ContentNegotiatingViewResolver。找到如下方法!

@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使用所有其他視圖解析器來定位視圖,是以它應該具有較高的優先級
    resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return resolver;
}
           

我們可以點進這類看看!找到對應的解析視圖的代碼;

@Nullable // 注解說明:@Nullable 即參數可為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 = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
    if (requestedMediaTypes != null) {
        // 擷取候選的視圖對象
        List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
        // 選擇一個最适合的視圖對象,然後把這個對象傳回
        View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
        if (bestView != null) {
            return bestView;
        }
    }
    // .....
}
           

我們繼續點進去看,他是怎麼獲得候選的視圖的呢?

getCandidateViews中看到他是把所有的視圖解析器拿來,進行while循環,挨個解析!

Iterator var5 = this.viewResolvers.iterator();
           

是以得出結論:ContentNegotiatingViewResolver 這個視圖解析器就是用來組合所有的視圖解析器的

我們再去研究下他的組合邏輯,看到有個屬性viewResolvers,看看它是在哪裡進行指派的!

protected void initServletContext(ServletContext servletContext) {
    // 這裡它是從beanFactory工具中擷取容器中的所有視圖解析器
    // ViewRescolver.class 把所有的視圖解析器來組合的
    Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(), ViewResolver.class).values();
    ViewResolver viewResolver;
    if (this.viewResolvers == null) {
        this.viewResolvers = new ArrayList(matchingBeans.size());
    }
    // ...............
}
           

既然它是在容器中去找視圖解析器,我們是否可以猜想,我們就可以去實作一個視圖解析器了呢?

我們可以自己給容器中去添加一個視圖解析器;這個類就會幫我們自動的将它組合進來;我們去實作一下

詳情請見:

Gitee-代碼

SpringBoot在自動配置很多元件的時候,先看容器中有沒有使用者自己配置的(如果使用者自己配置@bean),如果有就用使用者配置的,如果沒有就用自動配置的;

如果有些元件可以存在多個,比如我們的視圖解析器,就将使用者配置的和自己預設的組合起來!

擴充使用SpringMVC 官方文檔如下:

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.

如果您想保留 Spring Boot MVC 功能并添加額外的 MVC 配置(攔截器、格式化程式、視圖控制器和其他功能),您可以添加您自己的 WebMvcConfigurer 類型的 @Configuration 類,但不添加 @EnableWebMvc。 如果您希望提供 RequestMappingHandlerMapping、RequestMappingHandlerAdapter 或 ExceptionHandlerExceptionResolver 的自定義執行個體,您可以聲明一個 WebMvcRegistrationsAdapter 執行個體來提供此類元件。 
           

我們要做的就是編寫一個@Configuration注解類,并且類型要為WebMvcConfigurer,

還不能标注@EnableWebMvc注解;我們去自己寫一個;我們建立一個包叫config,寫一個類MyMvcConfig;

//應為類型要求為WebMvcConfigurer,是以我們實作其接口
//可以使用自定義類擴充MVC的功能
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 浏覽器發送/test , 就會跳轉到test頁面;
        registry.addViewController("/test").setViewName("test");
    }
}
           

我們可以去分析一下原理:

1、WebMvcAutoConfiguration 是 SpringMVC的自動配置類,裡面有一個類WebMvcAutoConfigurationAdapter

2、這個類上有一個注解,在做其他自動配置時會導入:@Import(EnableWebMvcConfiguration.class)

3、我們點進EnableWebMvcConfiguration這個類看一下,它繼承了一個父類:DelegatingWebMvcConfiguration

這個父類中有這樣一段代碼:

public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
    
  // 從容器中擷取所有的webmvcConfigurer
    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }
}
           

4、我們可以在這個類中去尋找一個我們剛才設定的viewController當做參考,發現它調用了一個

protected void addViewControllers(ViewControllerRegistry registry) {
    this.configurers.addViewControllers(registry);
}
           

5、我們點進去看一下

public void addViewControllers(ViewControllerRegistry registry) {
    Iterator var2 = this.delegates.iterator();

    while(var2.hasNext()) {
        // 将所有的WebMvcConfigurer相關配置來一起調用!包括我們自己配置的和Spring給我們配置的
        WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
        delegate.addViewControllers(registry);
    }

}
           

是以得出結論:所有的WebMvcConfiguration都會被作用,不止Spring自己的配置類,我們自己的配置類當然也會被調用;

官方文檔:

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

全面接管即:SpringBoot對SpringMVC的自動配置不需要了,所有都是我們自己去配置!

隻需在我們的配置類中要加一個@EnableWebMvc。

狂神說SpringBoot12:MVC自動配置原理 (qq.com)

上一篇: Nginx-初見
下一篇: JVM-初見