色综合图-色综合图片-色综合图片二区150p-色综合图区-玖玖国产精品视频-玖玖香蕉视频

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

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

瀏覽:25日期:2023-05-08 08:52:26

前言

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

正文

引出問(wèn)題

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

上面是SpringBoot MVC的自動(dòng)配置,問(wèn)題是這樣的,當(dāng)我們需要自己配置MVC時(shí),有三種選擇:

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

在老版本中我們常用的做法就是繼承WebMvcConfigurerAdapter類,這個(gè)類本身是實(shí)現(xiàn)了WebMvcConfigurer接口的,因?yàn)槔习姹綣DK接口沒(méi)有默認(rèn)方法,直接實(shí)現(xiàn)WebMvcConfigurer比較繁瑣,而后來(lái)接口可以有默認(rèn)方法了,WebMvcConfigurerAdapter就被標(biāo)記為過(guò)時(shí)了,所以我們現(xiàn)在配置MVC只需要實(shí)現(xiàn)WebMvcConfigurer接口或者繼承WebMvcConfigurationSupport,但是后者會(huì)導(dǎo)致SpringBoot的配置失效,因?yàn)樵谧詣?dòng)配置類上有@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)這樣一個(gè)注解,表示沒(méi)有WebMvcConfigurationSupport類及其子類的實(shí)例時(shí)才會(huì)加載自動(dòng)配置(另外使用@EnableWebMvc注解也會(huì)導(dǎo)致自動(dòng)配置失效)。

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

分析過(guò)程

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

接著我又想,是不是配置類的注冊(cè)順序在自動(dòng)配置的后面。這里我就犯了一個(gè)顯而易見(jiàn)的錯(cuò)誤,因?yàn)槲铱紤]的是注冊(cè)的順序,不是實(shí)例化。因?yàn)镃onditionalOnMissingBean注解是沒(méi)有指定bean的實(shí)例時(shí)才會(huì)去加載,而我腦海里當(dāng)時(shí)想成了ConditionalOnMissingClass。所以我在DefaultListableBeanFactory中的registerBeanDefinition和preInstantiateSingletons方法上打上了斷點(diǎn),力圖確認(rèn)注冊(cè)順序如我所想:

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 phasethis.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);....}

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

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

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

然后啟動(dòng)項(xiàng)目就可以看到首先實(shí)例化的果然是WebMvcAutoConfiguration類,這樣就搞清楚了為什么EnableWebMvcConfiguration沒(méi)有導(dǎo)致自動(dòng)配置失效。

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

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

粗略看一下調(diào)用棧信息,如果對(duì)Spring源碼熟悉,可以發(fā)現(xiàn)自動(dòng)配置類的實(shí)例化是在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實(shí)例化的那一篇分析過(guò),這個(gè)方法的作用是通過(guò)factoryMethod實(shí)例化當(dāng)前的BeanDefinition,而實(shí)例化該BD優(yōu)先會(huì)實(shí)例化factoryBeanName屬性指向的Bean,這里的factoryBeanName就是org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,factoryMethod則是formContentFilter,而這兩個(gè)屬性的設(shè)置則是在ConfigurationClassPostProcessor解析@Configuration和@Bean就設(shè)置好了(@Bean標(biāo)注的方法名會(huì)設(shè)置到factoryMethod,而該方法所在配置類的名稱就是factoryBeanName),這里就不展開(kāi)分析了。

@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自動(dòng)配置類中配置的,默認(rèn)是加載的,而filter就不用多說(shuō)了,在Tomcat啟動(dòng)后就會(huì)觸發(fā)初始化,追蹤調(diào)用棧也可以看到。另外我們還看到自動(dòng)配置類中還配置了一個(gè)HiddenHttpMethodFilter,不過(guò)這個(gè)默認(rèn)是不加載的,所以我們只要在application.properties中配置了如下屬性,自動(dòng)配置類就不會(huì)實(shí)例化了,但是兩個(gè)靜態(tài)內(nèi)部類的實(shí)例化還是不會(huì)受影響的。

spring.mvc.formcontent.filter.enabled=false

總結(jié)

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

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

標(biāo)簽: Spring
相關(guān)文章:
主站蜘蛛池模板: 欧美一级在线毛片免费观看 | aa国产 | 午夜在线伦理福利视频 | 欧美一级特黄特黄做受 | 欧美精品成人一区二区在线观看 | 欧美私人网站 | 国产精品久久九九 | 国产又色又爽黄的网站免费 | 国产成人免费永久播放视频平台 | 中文字幕一二三区乱码老 | 99久久免费午夜国产精品 | 久久ri精品高清一区二区三区 | 国产欧美一区二区三区视频在线观看 | 99国产在线观看 | 久久国产成人午夜aⅴ影院 久久国产成人亚洲精品影院老金 | 日韩一区二区三区在线播放 | 91精品国产爱久久久久久 | 久草热久| 真实国产乱人伦在线视频播放 | 在线视频久久 | 国产精品美乳免费看 | 国产一级一级一级成人毛片 | 久久99亚洲精品久久 | 亚洲视频在线a视频 | 成人影院在线免费观看 | 看性过程三级视频在线观看 | 特级片免费看 | 国产精品亚洲二区 | 一级做性色a爰片久久毛片 一级做性色a爰片久久毛片免费 | 日本免费人成黄页网观看视频 | 国产成人精品区在线观看 | 国产一进一出视频网站 | 国产精品区牛牛影院 | 91无毒不卡 | 日韩一区二区三区在线播放 | 国产国产成人人免费影院 | 一级一黄在线观看视频免费 | 亚洲精品一区二区手机在线 | 成年男女免费视频网站 | 亚洲精品久久久久网站 | 国产精品一区二区av |