@EnableWebMvc 與 @EnableAutoConfiguration 的關係

先講自動配置:@EnableAutoConfiguration,引入兩個 JAVA 類

@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

}
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

目前還不清楚這兩個類的原理,但是可以知道的是,Spring 通過這兩個類查找所有 JAR 包的 classpath:META-INF/spring.factories,找到 spring-boot-autoconfigure-xxx.jar 的 META-INF/spring.factories,幷包含如下內容

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
......
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
......

由此,Spring 加載了 WebMvc 的自動配置類

@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 {
    //......
}

值得注意的是 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class),記住這個註解條件,請看以下 Spring 繼承體系

這裏寫圖片描述

記住這個繼承關係,接着再來看 @EnableWebMvc,它引入了一個 Spring JavaConfig 類

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

Spring 會加載 DelegatingWebMvcConfiguration.class 這個配置類,由之前的繼承體系我們得知,它是 WebMvcConfigurationSupport 的子類,根據先前看到的自動配置的註解條件:只有當 WebMvcConfigurationSupport 這個 Bean 不存在時才加載 WebMvc 的自動配置類,否則忽略;換句話說,DelegatingWebMvcConfiguration.class 的加載取消了 WebMvc 的自動配置,也可以說 @EnableWebMvc 強制使用了另一套配置,而非 @EnableAutoConfiguration 指引的自動配置


呢麼 @EnableWebMvc 有什麼好處麼?當然有,WebMvcConfigurationSupport(DelegatingWebMvcConfiguration 的父類) 定義了一套創建 Bean 的標準化流程,例如 RequestMappingHandlerMapping、resourceHandlerMapping、HttpMessageConverter 等等,並通過 “模板方法模式” 將其過程開放給子類,以 HttpMessageConverter 爲例,請看 WebMvcConfigurationSupport 的 getMessageConverters()

protected final List<HttpMessageConverter<?>> getMessageConverters() {
    if (this.messageConverters == null) {
        this.messageConverters = new ArrayList<HttpMessageConverter<?>>();
        //這個就是模板方法,是抽象方法,必須由子類重寫
        configureMessageConverters(this.messageConverters);
        if (this.messageConverters.isEmpty()) {
            //如果用戶未定義任何 messageConverters,則進行默認初始化
            //默認初始化包含了 ByteArrayHttpMessageConverter、ResourceHttpMessageConverter 等
            addDefaultHttpMessageConverters(this.messageConverters);
        }
        //鉤子方法
        extendMessageConverters(this.messageConverters);
    }
    return this.messageConverters;
}

//模板方法是抽象的,必須由子類重寫
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}

可以看到,這就是創建 MessageConverters 的標準化流程,再看 @EnableWebMvc 導入的 DelegatingWebMvcConfiguration(即 WebMvcConfigurationSupport 子類)是如何重寫 該模板方法的

@Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    this.configurers.configureMessageConverters(converters);
}

可以看到,它直接把所有的 HttpMessageConverter 交給 this.configurers 進行處理,無論是添加還是刪除,是修改還是無所作爲,這都是可行的,注意,這裏暗示了我們開發者介入的契機,呢麼 this.configurers 又是什麼呢

private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();


@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
    if (!CollectionUtils.isEmpty(configurers)) {
        this.configurers.addWebMvcConfigurers(configurers);
    }
}

簡單來講,WebMvcConfigurerComposite 就是一個容納許多 WebMvcConfigurer 的容器,Spring 通過 @Autowired 來自動注入所有的 WebMvcConfigurer,並全部添加進容器裏;這意味着,我們可以定義任意多的 WebMvcConfigurer,它們全部都會被納入容器中,並依次接受 Spring 分派所有的 HttpMessageConverter 來處理,我們可以在處理中添加我們自己想要的 HttpMessageConverter,再來看一次該模板方法代碼

@Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    this.configurers.configureMessageConverters(converters);
}

configurers 只是個容器,這裏可能會被誤導,查看其源碼

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    for (WebMvcConfigurer delegate : this.delegates) {
        delegate.configureMessageConverters(converters);
    }
}

很明顯,它依次調用容器中所有的 WebMvcConfigurer 的 configureMessageConverters(),所以我們要做的是:定義一個或多個 Bean 繼承 WebMvcConfigurer 並重寫 configureMessageConverters(),在這裏面添加我們想要的 HttpMessageConverter,然後等着被自動注入,被模板方法依次調用,我們的奇思妙想就成功了,注意,一般只需要重寫很少的抽象方法,因此推薦繼承 WebMvcConfigurer 的子類 WebMvcConfigurerAdapter,並重寫有需要的一個或幾個方法

@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {

    // 自定義 JSON 轉換工具
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(fastJsonHttpMessageConverterEx());
        super.configureMessageConverters(converters);
    }

    @Bean
    public FastJsonHttpMessageConverterEx fastJsonHttpMessageConverterEx() {
        return new FastJsonHttpMessageConverterEx();
    }

}

注意 @Configuration 是必要的,只有這樣才能將其裝配成 Bean,進而被 @Autowired 自動注入

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章