1. BeanFactoryPostProcessor調用過程源碼剖析
2. 配置類的解析過程源碼
3. 配置類@Configuration加與不加的差別
4. 重複beanName的覆寫規則
5. @ComponentScan的解析原理
我們經常會在一個類上打上@Configuration, @Component, @Bean等. 帶有這些注解的類, 就是我們所說的配置類. 那麼, spring啟動的時候,是如何加載這些配置類的呢?
下面就以此為目的, 分析spring源碼. 本節的内容是對上一節内容的實戰分析, 同時更加詳細的解讀spring源碼

我們知道, spring啟動的時候做了3件事, 就是上面的三件事.
我們先定義好要分析加載的配置類
這個配置類很簡單, 使用@ComponentScan注解指定了掃描的包. @Configuration指定目前是一個配置類
在main裡, 通過AnnotationConfigurationApplicationContext讀取配置類MainConfig.class.
配置類被傳進來以後, 到底是怎麼被解析的呢? 這就是我們分析的線索
始終不要忘記我們的整體架構圖. 對照這個圖來分析. 思路更清晰. 整體内容講解在這裡: https://www.cnblogs.com/ITPower/p/13677635.html
下面, 從入口進入. 我們的入口就是這裡
下面進入AnnotationConfigApplicationContext的構造方法
在初始化AnnotatedBeanDefinitionReader(this);的時候, 注冊了很多後置處理器
我們看到, 注冊了6個原始RootBeanDefinition, 這些bean是spring自己提前定義好的, 他們的加載是整個spring的基礎. 用于解析spring中其他的類
而這一次我們要研究配置類是如何被讀取的, 是以重點關注的是下面這個後置處理器
這裡還有很多其他的原始類被注冊了, 但我們的目标是分析配置類是如何被讀取的, 是以, 其他的先忽略, 隻看ConfigurationClassPostProcessor.
可以看到ConfigurationClassPostProcessor是同時實作了BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor. 這一點我們需要記住, 後面會使用到
首先,建構了一個RootBeanDefinition. 然後調用了registerPostProcessor方法, 三個入參分别是
這裡面的關鍵代碼是标紅的部分, 将ConfigurationClassPostProcessor放入到了beanDefinitionMap裡面
下面的else是處理循環引用的問題, 暫時先不要看.
在this()構造方法裡, 還初始化了ClassPathBeanDefinitionScanner, 這裡隻說一句.
我們在掃描配置類的時候, 确實使用的是ClassPathBeanDefinitionScanner, 但是, 不是this.scanner對象. 而是自己new的一個ClassPathBeanDefinitionScanner.
這裡的scanner僅僅是為了程式員可以手動調用AnnotationConfigApplicationContext對象的scan方法
通過調用context.scan("package name");掃描處理配置類
使用方式如下:
到目前為止完成了後置處理器注冊為BeanDefinition
備注:
ConfigurationClassPostProcessor是一個工具類, 這個類的作用是解析配置類.
工具類有了, 那麼還得有主角呀, 那就是我們上面的配置類. 下面看看配置類的加載
注冊配置類,入口自然是這裡了
跟蹤進去找到doRegisterBean(...)方法
重點就是紅色這句話, 其他可以略過, 因為我們的配置類很簡單, 直接看BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
我們找到 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());方法, 進入到DefaultListableBeanFactory檢視方法, 這個方法之前我們已經調用過一次
就是在注冊ConfigurationClassPostProcessor的時候, 我們需要将其解析為BeanDefinition然後放到BeanDefinitionMap中, 這裡也是一樣的, 将我們的配置類MainConfig解析成BeanDefinition放入到BeanDefinitionMap中.
這裡的代碼在整個架構中處于什麼位置呢? 将MainConfig解析為BeanDefinition放入到BeanDefinitionMap中
以上兩步, 一個是将ConfigurationClassPostProcessor配置類後置處理器, 也就是解析配置的工具類, 解析成BeanDefinition放入到BeanDefinitionMap中
另一個是将我們的目标配置類MainConfig加載到記憶體, 組裝成BeanDefinition放入到BeanDefinitionMap中.
到這裡,我們完成了兩步.
第一步: 準備工具類ConfigurationClassPostProcessor
第二步: 準備配置類MainConfig.
接下倆, 就是要使用工具類來解析配置類MainConfig了
在refresh()中有很多步驟, 我們重點來看invokeBeanFactoryPostProcessors(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);看名字, 調用的是Bean工廠的後置處理器, 上面分析了, 初始化的時候初始化了很多spring原生的後置處理器, 這麼多後置處理器, 其實, 隻有一個後置處理器實作了BeanFactoryPostProcessor, 它就是ConfigurationClassPostProcessor, 還記得上面的結構圖麼, 拿下來, 再看一遍.
這裡調用的時候, 原生處理器隻會調用ConfigurationClassPostProcessor
這裡要調用bean工廠的後置處理器了. 看上面的注釋, 注釋寫的很清晰.
在調用PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());的時候調用了getBeanFactoryPostProcessors()方法.
接下來重點來了. PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); 方法實作一共分為兩大步:
來看看源碼是如何定義的. 重點看代碼的注釋, 每一部分的功能都有明确标出, 注釋寫的很詳細
下面我們就來分析上圖所示的内容.
首先, 拿到了所有實作了BeanDefinitionRegistryPostProcessor的後置處理器, 上面我們做過鋪墊,隻有ConfigurationClassPostProcessor實作了BeanDefinitionRegistryPostProcessor後置處理器
是以,這裡過濾出來的postProcessorNames隻有一個,就是ConfigurationClassPostProcessor, 接下來, 判斷這個類是否實作了PriorityOrdered 優先排序的接口, 如果實作了, 那麼放入到currentRegistryProcessors中, 後面會進行調用.
接下來, 執行invokeBeanDefinitionRegistryPostProcessors
這是第一次調用BeanDefinitionRegistryPostProcessors
第二次調用的時候 ,依然是擷取所有的實作了BeanDefinitionRegistryPostProcessor接口的後置處理器, 且這個處理器沒有實作過PriorityOrdered也就是沒有被上面調用過. 且實作了Ordered接口
這一類添加到currentRegistryProcessors集合中, 然後調用invokeBeanDefinitionRegistryPostProcessors處理
這是第二次調用BeanDefinitionRegistryPostProcessor
第三次調用的是沒有實作過任何排序接口的後置處理器. 并将其放入到currentRegistryProcessors, 然後執行invokeBeanDefinitionRegistryPostProcessors
ConfigurationClassPostProcessor同時實作了BeanDefinitionRegistryPostProcessor 和 BeanFactoryPostProcessors, 調用的是invokeBeanFactoryPostProcessors
一共進行了4次調用
總結: 優先處理的是實作了PriorityOrdered的後置處理器, 然後調用實作了Order接口的後置處理器, 最後調用了沒有實作任何排序方法的後置處理器. 最後調用工廠類方法.
下面我們來具體分析invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
我們在這一步打個斷點, 然後跟着斷點一步一步點選進去
這是registryProcessors裡面隻有一個後置處理器, 就是ConfigurationClassPostProcessor.
然後進入到ConfigurationClassPostProcessor.postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)方法
這裡先看enhanceConfigurationClasses(beanFactory);個方法
粗體部分就是判斷是否需要進行cglib代理. 進行cglib代理的條件是, beanDefinition中屬性configurationClass的值是full. 隻有full版配置才會建立cglib代理
那麼有下面幾個問題:
問題1: full版本配置是什麼呢?
我們使用@Configuration注解了, 在加載的時候, 就會将configurationClass屬性設定為full.當設定為full以後, 我們在調用的時候, 就會建立一個cglib動态代理.
問題2: 為什麼要建立動态代理呢?
動态代理可以保證, 每次建立的bean對象隻有一個
問題3:那麼加@Configuration和不加本質上的差別是什麼?
當在配置類中一個@Bean使用方法的方式引入另一個Bean的時候, 如果不加@Configuration注解, 就會重複加載Bean.如果加了@Configuration, 則會在這裡建立一個cglib代理, 當調用了@Bean方法是會先檢測容器中是否存在這個Bean, 如果不存在則建立, 存在則直接使用.
這是在上面調用invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);接口的時候, 标記的是full還是Lite
下面來看一下源碼
在這裡一步,執行的時候,進行了這個類是full的還是lite,繼續忘下看
此時滿足條件的postProcessor隻有一個, 那就是ConfigurationClassPostProcessor. 下面直接看ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry()方法
前面都是一些條件判斷, 重點看processConfigBeanDefinitions(registry);
在這裡,這個方法判斷了, 這個類是full的還是lite的. 下面直接上代碼
上面主要是擷取中繼資料, 然後判斷中繼資料中是否有Configuration注解. 如果有,傳回其屬性. 我們判斷其屬性中proxyBeanMethods是否true, 如果是true, 那麼将其設定為full.
如果配置中帶有@Component, @ComponentScan @Import @ImportResource @Bean這幾種屬性之一, 那麼就将其設定為lite.
這也是@Configuration 和其他注解類似@Component和@ComponentScan的本質差別:
當在配置類中一個@Bean使用方法的方式引入另一個Bean的時候, 如果不加@Configuration注解, 就會重複建立Bean
如果加了@Configuration, 則會在這裡建立一個cglib代理, 當調用了@Bean方法是會先檢測容器中是否存在這個Bean, 如果不存在則建立, 存在則直接使用.
下面來看個例子
這是定義的car和tank的基礎類
當配置類使用了@Configuration注解的時候, 運作main方法
當去掉@Configuration注解的時候, 再次運作, 我們看到建立了兩次tank
在main方法中調用了兩次(Car) context.getBean("car");
在new一個對象的時候, 如果不取ioc容器中取, 那麼每一次都會建立一個新的.
在ioc容器中, car對象隻有一個, 但是在建構car的時候, 調用了tank, tank在ioc容器中卻不一定隻有一份. 隻有使用了@Configuration, 表示需要使用cglib動态代理查找tank類, 保證ioc容器中隻有一份.
通過跟蹤@ComponentScan注解是如何解析的, 分來了解BeanDefinitionScan, BeanDefinitionRegistry, BeanDefinitionReader是如何工作的.
這裡也有兩大步
第一步: 初始化bean工廠的後置處理器
通過調用beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class) 初始化了bean工廠的後置處理器,
第二步: 解析配置
調用invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);調用beanDefinitionRegistry的後置處理器. 篩選出符合條件的配置類.
如上圖所示, 最後篩選出的配置類隻有MainConfig配置類. 也就是說configCandidates配置候選集合中隻有一個MainConfig
然後, 接下來建立了一個對象ConfigurationClassParser, 這是一個配置類解析器. 下面将使用這個解析器解析配置類.
重點是如何解析的, 代碼已重點标注出來了.
我們這裡是通過注解解析的, 是以直接看下面的代碼
解析主要做了幾件事呢?如下圖:
解析配置類, 看看配置類是否含有如上标記的注解, 如果有, 則調用響應的傳回對其進行解析,處理.
下面來看看源碼. 是如何處理這一塊的.
下面我們重點看對@ComponentScan和@ComponentScans注解的解析, 為什麼看他呢? 因為很多注解都标記了@Component注解.
比如@Service注解,本身使用@Component
再來看@Controller注解, 其實質也是一個@Component注解
我們在自定義配置類的時候, 會使用@ComponentScan注解. 并傳遞一個包, 作為掃描包. 如MainConfig配置
這就會掃描包下所有的配置類.
它主要的邏輯如下:
在拿到@ComponentScan注解以後, 會對其進行parse. 主要解析裡面的注解. 并對每一個注解進行處理. 處理後将其添加到scanner屬性中. 最後調用scanner.doScan(....)方法.
源碼如下:
調用doScan方法掃描配置類. 我們來看看主要做了哪些事情
第一步: 找到所有候選的BeanDefinition.
上面解析出了@ComponentScan注解傳遞過來的basePackages包. 掃描包中所有的類, 得到候選類.
掃描的時候做了幾件事呢? 看最上圖最右側部分. 這掃描出來就是我們的目标類.
第二步: 解析這些準目标類.
第三步: 設定預設的beanDefinition屬性
第四步: 将解析出來的bean定義注冊到ioc容器中
這裡就調用了BeanDefinitionReaderUtils.registerBeanDefinition注冊bean定義. 之前注冊過配置類, 這裡和其是一樣的. 是以不再贅述了
這裡有兩個細節:
1. excludeFilter中排除了自己
在解析配置類的時候, 除了@ComponentScan注解中定義的ExcludeFilter和IncludeFilter以外, 還有預設的排除類. 如上加粗字型的部分. 這裡是排除了配置類本身, 我們這裡的配置類是MainConfig, 也就說, 會排除掉自己.
matchClassName是一個鈎子方法. 在執行到這裡的時候, 不會真的去執行. 什麼時候執行呢? 後面調用doScan的時候執行.
在尋找候選配置類的時候, 進行了排除了配置類本身.
進入這個方法
在第四步的時候調用了isCandidateComponent(metadataReader, 這裡就判斷了是否是包含的類,或者是排除的類
紫色加錯的部分是就是判讀是否符合排除的類. 紅色加錯的部分是判斷是否是包含的類.
先來看紫色的部分, 排除的類
看到了麼, 這裡調用了matchClassName. 這就是上面定義的鈎子方法,
此時declaringClass表示的是目前的配置類, className表示的是目标類, 如果目前目标類 == 配置類, 那麼就放回true. 傳回true, 則會排除掉
2. includeFilter中包含了預設的配置類
下面來看紅色加錯的部分
我們看到這裡有this.includeFilters.包含的過濾器. 這裡面是有值的
我們沒有在配置類MainConfig上設定includeFilter啊, 這裡面怎麼會有值呢?
這是因為我們有預設包含的過濾器, 下面看看預設包含的過濾器是在哪裡設定的.
首先從入口類點選AnnotationConfigApplicationContext
然後在點選this();
再點選ClassPathBeanDefinitionScanner
然後一路點選三個this(...)
最後看到上圖 registerDefaultFilter();注冊預設的過濾器
如上圖看到, 注冊了3個預設的過濾器. 分别是Component, ManagedBean, Named. 他們都是注解類型的過濾器AnnotationTypeFilter
其中javax.annotation.ManagedBean和javax.inject.Named是jdk提供給我們的.