最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

全網(wǎng)最深分析SpringBoot MVC自動配置失效的原因

 更新時間:2020年07月01日 10:48:01   作者:夜勿語  
這篇文章主要介紹了全網(wǎng)最深分析SpringBoot MVC自動配置失效的原因,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

前言

本來沒有計劃這一篇文章的,只是在看完SpringBoot核心原理后,突然想到之前開發(fā)中遇到的MVC自動失效的問題,雖然網(wǎng)上有很多文章以及官方文檔都說明了原因,但還是想親自看一看,本以為很簡單的事情,沒想到卻引發(fā)出一個較復雜的問題,請教了很多人都沒有得到結(jié)果,網(wǎng)上文章也沒有寫清楚的,最后還是自己搞了很久才弄明白的,此篇主要記錄自己的一個分析過程。

正文

引出問題


上面是SpringBoot MVC的自動配置,問題是這樣的,當我們需要自己配置MVC時,有三種選擇:

  • 實現(xiàn)WebMvcConfigurer接口
  • 繼承WebMvcConfigurerAdapter類
  • 繼承WebMvcConfigurationSupport類

在老版本中我們常用的做法就是繼承WebMvcConfigurerAdapter類,這個類本身是實現(xiàn)了WebMvcConfigurer接口的,因為老版本JDK接口沒有默認方法,直接實現(xiàn)WebMvcConfigurer比較繁瑣,而后來接口可以有默認方法了,WebMvcConfigurerAdapter就被標記為過時了,所以我們現(xiàn)在配置MVC只需要實現(xiàn)WebMvcConfigurer接口或者繼承WebMvcConfigurationSupport,但是后者會導致SpringBoot的配置失效,因為在自動配置類上有@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)這樣一個注解,表示沒有WebMvcConfigurationSupport類及其子類的實例時才會加載自動配置(另外使用@EnableWebMvc注解也會導致自動配置失效)。

MVC自動配置失效的原因就是這個了,基本上所有網(wǎng)上的文章分析到這一步也就完了,但是注意上圖我畫的紅方框,在這個自動配置類中有兩個靜態(tài)內(nèi)部類,我們知道靜態(tài)內(nèi)部類是優(yōu)于外部類加載的(SpringBoot自動配置大量使用了此特性),而其中EnableWebMvcConfiguration這個類,我注意到它是繼承自DelegatingWebMvcConfiguration,而DelegatingWebMvcConfiguration又繼承自WebMvcConfigurationSupport類,相信看到這你也應該會有疑惑了,為什么這個配置類沒有導致自動配置失效,而我們自己實現(xiàn)的就會?

分析過程

我知道配置類的解析注冊是在ConfigurationClassPostProcessor類中,而這個類我前面的文章多次分析過,雖然這個類的實現(xiàn)流程不難,但細節(jié)非常繞,所以之前沒有深挖。遇到這個問題時,我首先想的是對這個類的理解不夠深刻,因此第一時間想到仔細研究這個類,在花費了大量時間斷點分析后,卻沒有太大的收獲。

接著我又想,是不是配置類的注冊順序在自動配置的后面。這里我就犯了一個顯而易見的錯誤,因為我考慮的是注冊的順序,不是實例化。因為ConditionalOnMissingBean注解是沒有指定bean的實例時才會去加載,而我腦海里當時想成了ConditionalOnMissingClass。所以我在DefaultListableBeanFactory中的registerBeanDefinition和preInstantiateSingletons方法上打上了斷點,力圖確認注冊順序如我所想:

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);
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					removeManualSingletonName(beanName);
				}
			}
			else {
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				removeManualSingletonName(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}
	}

	public void preInstantiateSingletons() throws BeansException {
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
		....
	}


但結(jié)果beanDefinitionNames中的順序卻是兩個靜態(tài)內(nèi)部類在前,也就是說靜態(tài)內(nèi)部類肯定是在外部類之前就注冊到IOC容器中了,這下我就傻了。但幸好也是因此,否則我就該認為這就是結(jié)果了。最終我想到了應該看類的實例化順序,但是正常情況下類的實例化順序就是上面的斷點圖中的順序,我想會不會是有什么類依賴了WebMvcAutoConfiguration,導致它提前實例化。于是我將斷點又設置到AbstractBeanFactory中的doGetBean方法并加上了條件(不得不說idea的功能非常強大,回到上一個調(diào)用點、給斷點設置條件、調(diào)用堆棧信息大大節(jié)省了我的調(diào)試時間):


然后啟動項目就可以看到首先實例化的果然是WebMvcAutoConfiguration類,這樣就搞清楚了為什么EnableWebMvcConfiguration沒有導致自動配置失效。

但是還沒完,為什么自動配置類會在靜態(tài)內(nèi)部類之前實例化呢?是由誰觸發(fā)的呢?繼續(xù)深入,這時我想到了看調(diào)用棧:

粗略看一下調(diào)用棧信息,如果對Spring源碼熟悉,可以發(fā)現(xiàn)自動配置類的實例化是在instantiateUsingFactoryMethod中觸發(fā)的:

String factoryBeanName = mbd.getFactoryBeanName();
		if (factoryBeanName != null) {
			if (factoryBeanName.equals(beanName)) {
				throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
						"factory-bean reference points back to the same bean definition");
			}
			factoryBean = this.beanFactory.getBean(factoryBeanName);
			if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
				throw new ImplicitlyAppearedSingletonException();
			}
			factoryClass = factoryBean.getClass();
			isStatic = false;
		}
		else {
			// It's a static factory method on the bean class.
			if (!mbd.hasBeanClass()) {
				throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
						"bean definition declares neither a bean class nor a factory-bean reference");
			}
			factoryBean = null;
			factoryClass = mbd.getBeanClass();
			isStatic = true;
		}

這段代碼在bean實例化的那一篇分析過,這個方法的作用是通過factoryMethod實例化當前的BeanDefinition,而實例化該BD優(yōu)先會實例化factoryBeanName屬性指向的Bean,這里的factoryBeanName就是org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,factoryMethod則是formContentFilter,而這兩個屬性的設置則是在ConfigurationClassPostProcessor解析@Configuration和@Bean就設置好了(@Bean標注的方法名會設置到factoryMethod,而該方法所在配置類的名稱就是factoryBeanName),這里就不展開分析了。

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

	public static final String DEFAULT_PREFIX = "";

	public static final String DEFAULT_SUFFIX = "";

	private static final String[] SERVLET_LOCATIONS = { "/" };

	@Bean
	@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
	@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
	public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
		return new OrderedHiddenHttpMethodFilter();
	}

	@Bean
	@ConditionalOnMissingBean(FormContentFilter.class)
	@ConditionalOnProperty(prefix = "spring.mvc.formcontent.filter", name = "enabled", matchIfMissing = true)
	public OrderedFormContentFilter formContentFilter() {
		return new OrderedFormContentFilter();
	}
	
	......
}

formContentFilter就是在MVC自動配置類中配置的,默認是加載的,而filter就不用多說了,在Tomcat啟動后就會觸發(fā)初始化,追蹤調(diào)用棧也可以看到。另外我們還看到自動配置類中還配置了一個HiddenHttpMethodFilter,不過這個默認是不加載的,所以我們只要在application.properties中配置了如下屬性,自動配置類就不會實例化了,但是兩個靜態(tài)內(nèi)部類的實例化還是不會受影響的。

spring.mvc.formcontent.filter.enabled=false

總結(jié)

該問題只是出于興趣研究,雖然耗費了大量的時間和精力,但收獲不少,加深了對Spring源碼的理解,也修正了之前的一些錯誤理解,另外對于源碼更多的是要自己去研究,不能只看一兩篇文章或聽別人說,只有自己親手調(diào)試過才能知道自己的理解是否正確。

到此這篇關(guān)于全網(wǎng)最深分析SpringBoot MVC自動配置失效的原因的文章就介紹到這了,更多相關(guān)SpringBoot MVC自動配置失效內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關(guān)文章

  • idea中加入git版本控制的方法及步驟詳解

    idea中加入git版本控制的方法及步驟詳解

    在idea中加入git版本控制,方便團隊中多人協(xié)同開發(fā),項目可以同時方便進行管理和迭代。下面就是idea中加入git 的方法和步驟,感興趣的朋友一起看看吧
    2021-09-09
  • idea git未提交代碼文件名字變色(圖解)

    idea git未提交代碼文件名字變色(圖解)

    這篇文章主要介紹了idea git未提交代碼文件名字變色,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-04-04
  • 全面詳解Spring?Bean生命周期教程示例

    全面詳解Spring?Bean生命周期教程示例

    這篇文章主要為大家介紹了Spring?Bean生命周期的全面詳解教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-04-04
  • Mybatis 動態(tài)SQL搭建環(huán)境的全過程

    Mybatis 動態(tài)SQL搭建環(huán)境的全過程

    這篇文章主要給大家介紹了關(guān)于Mybatis動態(tài)SQL搭建環(huán)境的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-05-05
  • Java性能調(diào)優(yōu)概述

    Java性能調(diào)優(yōu)概述

    本文介紹了Java程序性能的主要表現(xiàn)點、衡量程序性能的主要指標、性能調(diào)優(yōu)的層次、基本調(diào)優(yōu)策略和手段等,具有很好的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • 淺析JAVA_HOME,CLASSPATH和PATH的作用

    淺析JAVA_HOME,CLASSPATH和PATH的作用

    以下是對JAVA_HOME,CLASSPATH和PATH的作用進行了詳細的分析介紹,需要的朋友可以過來參考下
    2013-07-07
  • java  Vector和ArrayList的分析及比較

    java Vector和ArrayList的分析及比較

    這篇文章主要介紹了java Vector和ArrayList的分析及比較的相關(guān)資料,Vector是多線程安全的,而ArrayList不是,本文主要做對比對這兩個方法,需要的朋友可以參考下
    2016-11-11
  • Java布爾值Boolean和boolean之間轉(zhuǎn)換實例用法

    Java布爾值Boolean和boolean之間轉(zhuǎn)換實例用法

    在本篇文章里小編給大家整理的是一篇關(guān)于Java布爾值Boolean和boolean之間轉(zhuǎn)換實例用法內(nèi)容,有需要的朋友們跟著學習參考下。
    2021-06-06
  • Spring Boot 2.0.0 終于正式發(fā)布-重大修訂版本

    Spring Boot 2.0.0 終于正式發(fā)布-重大修訂版本

    北京時間 2018 年 3 月 1 日早上,如約發(fā)布的 Spring Boot 2.0 在同步至 Maven 倉庫時出現(xiàn)問題,導致在 GitHub 上發(fā)布的 v2.0.0.RELEASE 被撤回
    2018-03-03
  • Springboot jar文件如何打包zip在linux環(huán)境運行

    Springboot jar文件如何打包zip在linux環(huán)境運行

    這篇文章主要介紹了Springboot jar文件如何打包zip在linux環(huán)境運行,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-02-02

最新評論

宜君县| 宜章县| 临海市| 疏附县| 通化县| 金湖县| 仙游县| 嵊州市| 莒南县| 井冈山市| 民乐县| 黄龙县| 丰顺县| 姜堰市| 射阳县| 同德县| 蓬安县| 武城县| 五原县| 九寨沟县| 九江县| 泊头市| 玉屏| 蕲春县| 兴隆县| 阳春市| 娄烦县| 扶沟县| 专栏| 安阳市| 交口县| 宁乡县| 绥德县| 克东县| 雷山县| 石门县| 凉城县| 莒南县| 贡觉县| 康平县| 孟州市|