先聲明本人并不是標題黨,如果看了本篇文章并且認為沒有得到任何收獲,請您隨便留言罵我,本人絕不還口,已經(jīng)對springboot了如指掌大大神,求放過!

            不BB了,直接上代碼,請各位在自己的springboot項目隨便一個包下復(fù)制進去如下類(不要修改什么東西),如果你的springboot還能站起來算我輸!
          @Component public class Environment { }

            運行springboot的啟動類會報如下錯誤,然后你刪除這個類,你的springboot又能健步如飛了,你可能就會懷疑人生了,這代碼有毒。先說明我的springboot是2.1.7.RELEASE,我也試了最新的2.2,報錯基本一致!
          2019-11-02 00:42:46.181 INFO 13568 --- [ main]
          com.rdpaas.platform.demo.RunApplication : Starting RunApplication on
          DESKTOP-9KL4U5L with PID13568 (E:\project2018\platform\demo\target\classes
          started by49519 in E:\project2018\platform) 2019-11-02 00:42:46.183 INFO 13568
          --- [ main] com.rdpaas.platform.demo.RunApplication : No active profile set,
          falling back to default profiles: default2019-11-02 00:42:48.490 WARN 13568 ---
          [ main] ConfigServletWebServerApplicationContext : Exception encountered during
          context initialization - cancelling refresh attempt:
          org.springframework.beans.factory.UnsatisfiedDependencyException: Error
          creating bean with name'methodValidationPostProcessor' defined in class path
          resource
          [org/springframework/boot/autoconfigure/validation/ValidationAutoConfiguration.class]:
          Unsatisfied dependency expressed through method'methodValidationPostProcessor'
          parameter0; nested exception is
          org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying
          bean of type'org.springframework.core.env.Environment' available: expected at
          least1 bean which qualifies as autowire candidate. Dependency annotations: {}
          2019-11-02 00:42:48.499 INFO 13568 --- [ main]
          ConditionEvaluationReportLoggingListener : Error starting ApplicationContext.
          To display the conditions report re-run your application with 'debug' enabled.
          2019-11-02 00:42:48.615 ERROR 13568 --- [ main]
          o.s.b.d.LoggingFailureAnalysisReporter :*************************** APPLICATION
          FAILED TO START*************************** Description: Parameter 0 of method
          methodValidationPostProcessorin
          org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration
          required a bean of type'org.springframework.core.env.Environment' that could
          not be found. Action: Consider defining a bean of type'
          org.springframework.core.env.Environment' in your configuration.

            如上很普通,誰都可能加上的類,就這樣一個簡單的類,居然可以直接導(dǎo)致springboot站不起來了,如果你認命了,其實也很好解決,你可能會換個名字試試,或者你壓根就不會用到這個類,或者是你給@Component("env"),加個別名,可能就碰巧解決了這個問題,那么這時候你可能會當成springboot已經(jīng)規(guī)定了你不能使用關(guān)鍵字environment作為bean的名稱,那么這個問題就變得一文不值了,因為你已經(jīng)認命了,不讓我用我不用就行了,以后一輩子都不用這個類名就好了。眼不見心不煩,我用簡稱Env還來的省事點。不過我個人認為我們遇到難題應(yīng)該迎難而上,不能隨便認命,我們都是驕傲的程序員。應(yīng)該抱著希望遇到難題的心態(tài),積極去面對難題,多解決一些疑難雜癥,用知識和經(jīng)驗武裝自己,努力成長,迎娶白富美,走上人生巔峰!如果看到這里覺得不認命的
          請跟著我一起看看這個問題到底為啥會出現(xiàn)吧!

            接下來我們一步步來找到問題的根源,為啥用了這個類,springboot就不舉了?首先我們從啟動的錯誤提示中找到唯一的關(guān)鍵信息:method
          methodValidationPostProcessor in
          org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration
          required a bean of type 'org.springframework.core.env.Environment' that could
          not be found。從這句話可以看出來在:
          org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration這個類中的這個方法:
          methodValidationPostProcessor 中需要一個:
          org.springframework.core.env.Environment類的對象作為參數(shù),但是他找不到,看這個名字和我們自己定義的一樣,先在idea中找到如上類的
          methodValidationPostProcessor方法的源碼所在:




          ?  為了驗證圖中的猜想,由于我這不是源碼編譯的,所以只能自己模仿這個類同樣使用@Bean修飾一個方法看看是不是里面的參數(shù)都是完全按照參數(shù)名稱注入的(可以先注釋掉之前的Environment類排除那個類的影響),如下
          package com.rdpaas.platform.demo.env; import
          org.springframework.stereotype.Component;/** * 用作測試的bean * @author: rongdi *
          @date: 2019-11-2 0:12*/ @Component public class TestBean { } package
          com.rdpaas.platform.demo.env;import org.springframework.context.annotation.Bean;
          import org.springframework.context.annotation.Configuration; /** *
          模仿org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration
          * 使用@Configuration注解,并且提供一個static的@Bean修飾的方法 *@author: rongdi * @date:
          2019-11-2 0:11*/ @Configuration public class Config { @Bean public static
          TestBean create(TestBean tb) { System.out.println(tb);return tb; } }

            如上我們使用默認的beanName為testBean的bean,然后Config類中注入的名稱是tb運行springboot發(fā)現(xiàn)可以正常打印出tb對象,說明名稱不一致同樣可以注入成功,所i有我們大概可以排除之前的猜想,想想頂頂大名的springboot也不可能這么low逼吧!


            之前的猜測被推翻,我們只能老老實實的使用debug一步步從springboot的入口一步步跟蹤進去看看到底啥時候開始報錯的,這一步如果不熟悉spring代碼的一定要耐心一步步找找,如下




          ?  之前對spring底層源碼有一定了解的應(yīng)該知道spring是先把那些注解和xml聲明的類加載到一個map里然后再進行初始化的,這個map就是beanDefinitionMap,一步步斷點到spring最核心的方法refresh中




          ?  當執(zhí)行到上圖藍色位置時,也就是執(zhí)行完invokeBeanFactoryPostProcessors(beanFactory)方法后,當前beanFactory里的beanDefinitionMap對象中找到了我們聲明的environment對象的身影,如下






            斷點的過程中發(fā)現(xiàn)代碼太多要是一步步找過去,很容易就放棄,所以我們再從上面的錯誤日志找找有用信息,然后通過全局搜索看看到底時哪里報出的錯誤,如通過報錯里的警告信息:expected
          at least 1 bean which qualifies as autowire candidate. Dependency
          annotations直接使用全局搜索(前提時先斷點跑一邊,然后根據(jù)idea提示下載好spring的源代碼)





          ?  點擊上述方法后如下一如既往的打個斷點重新跑一邊看看




          ?  這時候可以把斷點打在if那行再進去看看是啥情況導(dǎo)致進入了這里,然后我們可以確定只要是if返回了true,就必然會導(dǎo)致報錯,然后我們注釋掉自己的Environment類看看還能否進入到這里,通過注釋和不注釋的對比我們發(fā)現(xiàn)兩種情況斷點之后有如下差別



          ?  所以問題的關(guān)鍵就是加入了自己的Environment類導(dǎo)致matchingBeans的map為空而產(chǎn)生了本例中的報錯信息。






          ?  接下來我們?yōu)榱苏{(diào)試的效率,在每個出現(xiàn)beanName參數(shù)的方法打斷點都使用這個條件斷點,現(xiàn)在問題就回歸到為啥加上了自己的Environment類后給matchingBeans提供數(shù)據(jù)的方法findAutowireCandidates為空了。一如既往的條件斷點打到里面



          ?  使用同樣的方式在如下方法也加上條件斷點,再次重復(fù)執(zhí)行斷點直到進去如下



          ?  耐心的再用如上同樣方式進入這個方法,這里由于有多個類請使用F5(斷點進入方法,可能快捷鍵不一樣)



          ?

          ?

          ?  從上看出,剛進去循環(huán)的數(shù)組中明顯有environment,但是結(jié)果為啥就成了空數(shù)組,進一步斷點發(fā)現(xiàn)












          ?  對比以上兩個結(jié)果,很明顯當我們自己添加了Environment類后,singletonObjects肯定有一個移除操作,然后我們找到所有singletonObjects.remove()的地方打一個條件斷點:beanName.equals("environment"),很明顯從邏輯上看,只要springboot不是全部清空,必然會有一個
          ?remove("environment")才能解釋以上兩者的差別。

            然后我們再在singletonObjects.put()相關(guān)的方法都打上同樣的條件斷點,放心大膽的繼續(xù)重新斷點執(zhí)行一遍,第一次進入斷點如下


          ?  上圖如果執(zhí)行過addSingleton方法后this.singletonObjects中確實會放入以environment為key,以spring的StandardServetEnvironment為value的鍵值對進去,這里就不截圖了,免得又要重新跑一次斷點,直接點擊左邊調(diào)用棧那個679行后如下:







            這里可以先記錄下左邊環(huán)境對象到底是在spring最重要的refresh方法的那一步




            根據(jù)上面得出的結(jié)論,之所以報錯最根本的原因就是這個singletonObjects找不到這個environment了,而這里有,所以肯定有地方刪除了這個key,因為這個map看起來如此重要,spring不會無緣無故直接clear吧,所以只要找到唯一的刪除key的方式singletonObjects.remove(),并打上上面說的條件斷點,這一點上面其實說過了,那我們繼續(xù)跑斷點,直到找到在哪刪除了這個key







          ?







          ?其實復(fù)盤一下整個調(diào)試過程,發(fā)現(xiàn)其實源頭如下




          ?  其實我也不知道這算不算是springboot的bug,還是其實只是一個關(guān)鍵字的限定,因為最終解釋權(quán)不在于我,就像mybatis中的xml里大于符號要用>不然別人根本解析不了,從這一點來說mybatis使用xml存放sql實際上限制了我們使用大于小于等等這些符號的權(quán)力,只能用轉(zhuǎn)義字符類似別名的東西替代。其實這里也是類似,也可以理解成人家系統(tǒng)需要,你要用這個請改個名字或者取個別名,比如@Component("env)。不過我還是希望springboot能還我們使用單詞的自由,希望英文好的朋友可以發(fā)發(fā)郵件讓springboot團隊考慮下,哈哈!

          最后來個篇中總結(jié):

            1) 從文筆上來說,一如既往的沒有文筆,請各位大大海涵,真的盡力了,奈何胸無點墨!

            2) 從排版來說,一如既往的沒有排版,我是個純技術(shù)人,這些花里胡哨的東西,真的一點不會,同樣請各位包涵

            3)
          從知識點來說,其實這篇博客主要是給小白們分享一下看源碼的技巧和基本的調(diào)試能力,還有遇到問題的處理態(tài)度。首先從這篇文章中應(yīng)該能清晰的get到逆向思考一步步找的問題的方法,其次應(yīng)該能獲取到一些斷點調(diào)試源碼的技巧,最后也應(yīng)該能學(xué)會方法調(diào)用棧的作用。其實懂這三點基本就夠了,spring這些源碼是否看過也不會影響你最終能找到這個問題的根源這一結(jié)果,最多會影響你找到根源的時間。


            4)
          從用心程度來說,這篇博客自認為是足夠用心,周五晚上從下班回家一邊一步步斷點一遍寫這篇博客,直到凌晨三點多才沖忙洗洗睡。文章里基本上把我知道的關(guān)于這個知識點的所有東西通過清晰的圖文方式一步步展現(xiàn),本人熱愛技術(shù),也喜歡分享技術(shù),希望與廣大程序猿們相濡以沫,共同進步!

            5) 從文章質(zhì)量來說,對大牛一文不值,對小白有一定幫助,希望大牛們多多包涵,不要噴我,有錯誤之處請多多指正。

          友情鏈接
          ioDraw流程圖
          API參考文檔
          OK工具箱
          云服務(wù)器優(yōu)惠
          阿里云優(yōu)惠券
          騰訊云優(yōu)惠券
          京東云優(yōu)惠券
          站點信息
          問題反饋
          郵箱:[email protected]
          QQ群:637538335
          關(guān)注微信

                黄色一级片网站 | 久久精品免费看国产免费软件 | 99色精品视频 | 少妇一级淫片中文字幕 | 操逼操逼操逼操逼操逼操逼 |