全網最深分析SpringBoot MVC自動配置失效的原因
阿新 • • 發佈:2020-06-30
# 前言
本來沒有計劃這一篇文章的,只是在看完SpringBoot核心原理後,突然想到之前開發中遇到的MVC自動失效的問題,雖然網上有很多文章以及官方文件都說明了原因,但還是想親自看一看,本以為很簡單的事情,沒想到卻引發出一個較複雜的問題,請教了很多人都沒有得到結果,網上文章也沒有寫清楚的,最後還是自己搞了很久才弄明白的,此篇主要記錄自己的一個分析過程,。
# 正文
## 引出問題
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200629201827823.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2w2MTA4MDAz,size_16,color_FFFFFF,t_70#pic_center)
上面是SpringBoot MVC的自動配置,問題是這樣的,當我們需要自己配置MVC時,有三種選擇:
- 實現**WebMvcConfigurer**介面
- 繼承**WebMvcConfigurerAdapter**類
- 繼承**WebMvcConfigurationSupport**類
在老版本中我們常用的做法就是繼承**WebMvcConfigurerAdapter**類,這個類本身是實現了**WebMvcConfigurer**介面的,因為老版本JDK介面沒有預設方法,直接實現**WebMvcConfigurer**比較繁瑣,而後來介面可以有預設方法了,**WebMvcConfigurerAdapter**就被標記為過時了,所以我們現在配置MVC只需要實現**WebMvcConfigurer**介面或者繼承**WebMvcConfigurationSupport**,但是後者會導致SpringBoot的配置失效,因為在自動配置類上有@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)這樣一個註解,表示沒有**WebMvcConfigurationSupport**類及其子類的**例項**時才會載入自動配置(另外使用@EnableWebMvc註解也會導致自動配置失效)。
MVC自動配置失效的原因就是這個了,基本上所有網上的文章分析到這一步也就完了,但是注意上圖我畫的紅方框,在這個自動配置類中有兩個靜態內部類,我們知道靜態內部類是優於外部類載入的(SpringBoot自動配置大量使用了此特性),而其中**EnableWebMvcConfiguration**這個類,我注意到它是繼承自**DelegatingWebMvcConfiguration**,而**DelegatingWebMvcConfiguration**又繼承自**WebMvcConfigurationSupport**類,相信看到這你也應該會有疑惑了,為什麼這個配置類沒有導致自動配置失效,而我們自己實現的就會?
## 分析過程
我知道配置類的解析註冊是在**ConfigurationClassPostProcessor**類中,而這個類我前面的文章多次分析過,雖然這個類的實現流程不難,但細節非常繞,所以之前沒有深挖。遇到這個問題時,我首先想的是對這個類的理解不夠深刻,因此第一時間想到仔細研究這個類,在花費了大量時間斷點分析後,卻沒有太大的收穫。
接著我又想,是不是配置類的註冊順序在自動配置的後面。這裡我就犯了一個顯而易見的錯誤,因為我考慮的是**註冊**的順序,不是**例項化**。因為**ConditionalOnMissingBean**註解是沒有指定bean的例項時才會去載入,而我腦海裡當時想成了**ConditionalOnMissingClass**。所以我在**DefaultListableBeanFactory**中的**registerBeanDefinition**和**preInstantiateSingletons**方法上打上了斷點,力圖確認註冊順序如我所想:
```java
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
....
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);