Spring容器加載和實例化bean的順序

1、簡介
在使用Spring時,Bean之間會有些依賴,好比一個Bean A實例化時須要用到Bean B,那麼B應該在A以前實例化好。不少時候Spring智能地爲咱們作好了這些工做,但某些狀況下可能不是,好比Springboot的@AutoConfigureAfter註解,手動的指定Bean的實例化順序。瞭解Spring內Bean的解析,加載和實例化順序機制有助於咱們更好的使用Spring/Springboot,避免手動的去幹預Bean的加載過程,搭建更優雅的框架。框架

2、基本瞭解
Spring容器在實例化時會加載容器內全部非延遲加載的單例類型Bean。
ApplicationContext內置一個BeanFactory對象,做爲實際的Bean工廠,和Bean相關業務都交給BeanFactory去處理。BeanFactory在加載一個BeanDefinition(也就是加載Bean Class)時,將相應的beanName存入beanDefinitionNames屬性中,beanDefinitionName 屬性是Spring在加載Bean Class生成的BeanDefinition時,爲這些Bean預先定義好的名稱,在加載完全部的BeanDefinition後,執行Bean實例化工做在BeanFactory實例化全部非延遲加載的單例Bean時,遍歷beanDefinitionNames 集合,按順序實例化指定名稱的Bean,此時會依據beanDefinitionNames的順序來有序實例化Bean,也就是說Spring容器內Bean的加載和實例化是有順序的,並且近似一致,固然僅是近似。post

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {
        ......
        this.beanDefinitionNames.add(beanName);
    }
}this


3、加載bean過程.net

先看加載Bean Class過程,零配置下Spring Bean的加載起始於ConfigurationClassPostProcessor類的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry)方法,
其加載解析Bean Class的流程以下:對象


配置類能夠是Spring容器的起始配置類,也能夠是經過@ComponentScan掃描獲得的類,也能夠是經過@Import引入的類。
若是這個類上含有@Configuration,@Component,@ComponentScan,@Import,@ImportResource註解中的一個,或者內部含有@Bean標識的方法,那麼這個類就是一個配置類,Spring就會按照必定流程去解析這個類上的信息。在解析的第一步會校驗當前類是否已經被解析過了,若是是,那麼須要按照必定的規則處理(@ComponentScan獲得的Bean能覆蓋@Import獲得的Bean,@Bean定義的優先級最高)。
若是未解析過,那麼開始解析:
1.解析內部類,查看內部類是否應該被定義成一個Bean,若是是,遞歸解析。
2.解析@PropertySource,也就是解析被引入的Properties文件。
3.解析配置類上是否有@ComponentScan註解,若是有則執行掃描動做,經過掃描獲得的Bean Class會被當即解析成BeanDefinition,添加進beanDefinitionNames屬性中。以後查看掃描到的Bean Class是不是一個配置類(大部分狀況是,由於標識@Component註解),若是是則遞歸解析這個Bean Class。
4.解析@Import引入的類,若是這個類是一個配置類,則遞歸解析。
5.解析@Bean標識的方法,此種形式定義的Bean Class不會被遞歸解析
6.解析父類上的@ComponentScan,@Import,@Bean,父類不會被再次實例化,由於其子類可以作父類的工做,不須要額外的Bean了。blog

在1,3,4,6中都有遞歸操做,也就是在解析一個Bean Class A時,發現其上可以獲取到其餘Bean Class B信息,此時會遞歸的解析Bean Class B,在解析完Bean Class B後再接着解析Bean Class A,可能在解析B時可以獲取到C,那麼也會先解析C再解析B,就這樣不斷的遞歸解析。在第3步中,經過@ComponentScan掃描直接獲得的Bean Class會被當即加載入beanDefinitionNames中,但@Import和@Bean形式定義的Bean Class則不會,也就是說正常狀況下面@ComponentScan直接獲得的Bean其實例化時機比其餘兩種形式的要早。經過@Bean和@Import形式定義的Bean Class不會當即加載,他們會被放入一個ConfigurationClass類中,而後按照解析的順序有序排列,就是圖片上的 「將配置類有序排列」。一個ConfigurationClass表明一個配置類,這個類多是被@ComponentScan掃描到的,則此類已經被加載過了;也多是被@Import引入的,則此類還未被加載;此類中可能含有@Bean標識的方法。
Spring在解析完了全部Bean Class後,開始加載ConfigurationClass。若是這個ConfigurationClass是被Import的,也就是說在加載@ComponentScan時其未被加載,那麼此時加載ConfigurationClass表明的Bean Class。而後加載ConfigurationClass內的@Bean方法。遞歸

4、實例化Bean圖片

固然以上僅僅表明着加載Bean Class的順序,實際實例化Bean的順序和加載順序大致相同,但仍是會有一些差異。Spring在經過getBean(beanName)形式實例化Bean時,會經過BeanDefinition去生成Bean對象。在這個過程當中,若是BeanDefinition的DependsOn不爲空,從字面理解就是依賴某個什麼,
其值通常是某個或多個beanName,也就是說依賴於其餘Bean,此時Spring會將DependsOn指定的這些名稱的Bean先實例化,也就是先調用getBean(dependsOn)方法。咱們能夠經過在Bean Class或者@Bean的方法上標識**@DependsOn**註解,來指定當前Bean實例化時須要觸發哪些Bean的提早實例化。當一個Bean A內部經過@Autowired或者@Resource注入Bean B,那麼在實例化A時會觸發B的提早實例化,此時會註冊A>B的dependsOn依賴關係,實質和@DependsOn同樣,這個是Spring自動爲咱們處理好的。get

原文:https://blog.csdn.net/qq_27529917/article/details/79329809 it

相關文章
相關標籤/搜索