同包同名的配置類Spring是如何處理的?
先看一下測試用例





很遺憾加載了ServiceA,為什么會出現(xiàn)這種情況,我們下面分析一下
為什么會導致這種現(xiàn)象?
掃描的實現(xiàn)類為ClassPathBeanDefinitionScanner,Spring中所有的秒掃方法實現(xiàn)都是委托到這個類來處理的,我們直接看最原始的掃描結果.可以明顯看到這里雖然同包同名但是file的路徑是不同的,所以在掃描處理這個階段是可以區(qū)分的,但是實際上變?yōu)镾cannedGenericBeanDefinition這個對象的時候,它的基類AbstractBeanDefinition的equals方法經(jīng)過自身的重寫,并沒有去考慮實際Resource的不同.也就是存在同包同名的Class這里也只會注冊到容器一個對象(同包同名后面的會替換掉前面的).
可是雖然同包同名,但是我們通過@Profile進行了區(qū)分,這里應該是會把那個不滿足條件的Class跳過才對,也就是應該加載了對的那個Class.這里確實是加載的對的那一個,那么為什么加載了對了那個BeanDefinition我們的注解卻沒有生效呢?這里實際上只解析了指定路徑掃出來的結果,并沒有處理配置類的遞歸解析,我們繼續(xù)往下看看配置類的注解如何解析的.

這里看到一旦掃描完成直接判斷掃描到的BeanDefiition是否含有配置類@Configuration注解,如果有的話遞歸解析,但是parse方法并沒有直接使用這個BeanDefiition已經(jīng)解析出的注解而是自己從新加載



可以看到這里加載了錯誤的元信息回去,但是實際上這里解析不會出問題的,因為processConfigurationClass方法會判斷@Conditional注解的條件.

到這里都沒有載入錯誤的信息,那到底這些錯誤的注解實在哪里解析的?繼續(xù)往下看,追蹤到所有解析的入口在ConfigurationClassPostProcessor的processConfigBeanDefinitions方法.
先看一下主要的相關邏輯.去除了部分其他邏輯細節(jié)

在第一次掃描完之后原來外層還會再判斷一次配置類是否加載,這里可是從BeanDefiition取出來正確的元信息了,那么會加載正確嗎?


下面就找到了最終加載錯誤的元兇了...



可以看到最后還是通過正確的BeanDefinition跳過驗證,后面通過緩存加載了錯誤的類元信息,導致后面解析類元信息解析錯誤.
3總結
盡量避免同包,每個項目的package都應該有自己獨立的頂層package這樣能避免同包同名導致的錯誤.相同包內如果同名會提示錯誤的,所以同項目同包目錄下不擔心同名問題.
4其他
示例源碼:github源碼地址