在使用Spring時,Bean之間會有些依賴,好比一個Bean A實例化時須要用到Bean B,那麼B應該在A以前實例化好。不少時候Spring智能地爲咱們作好了這些工做,但某些狀況下可能不是,好比Springboot的@AutoConfigureAfter註解,手動的指定Bean的實例化順序。瞭解Spring內Bean的解析,加載和實例化順序機制有助於咱們更好的使用Spring/Springboot,避免手動的去幹預Bean的加載過程,搭建更優雅的框架。java
Spring容器在實例化時會加載容器內全部非延遲加載的單例類型Bean,看以下源碼:框架
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext, DisposableBean {
//刷新Spring容器,至關於初始化
public void refresh() throws BeansException, IllegalStateException {
......
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
}
}
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
/** List of bean definition names, in registration order */
private volatile List<String> beanDefinitionNames = new ArrayList<String>(256);
public void preInstantiateSingletons() throws BeansException {
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
for (String beanName : beanNames) {
......
getBean(beanName); //實例化Bean
}
}
}
複製代碼
ApplicationContext內置一個BeanFactory對象,做爲實際的Bean工廠,和Bean相關業務都交給BeanFactory去處理。在BeanFactory實例化全部非延遲加載的單例Bean時,遍歷beanDefinitionNames 集合,按順序實例化指定名稱的Bean。beanDefinitionNames 屬性是Spring在加載Bean Class生成的BeanDefinition時,爲這些Bean預先定義好的名稱,看以下代碼:post
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
......
this.beanDefinitionNames.add(beanName);
}
}
複製代碼
BeanFactory在加載一個BeanDefinition(也就是加載Bean Class)時,將相應的beanName存入beanDefinitionNames屬性中,在加載完全部的BeanDefinition後,執行Bean實例化工做,此時會依據beanDefinitionNames的順序來有序實例化Bean,也就是說Spring容器內Bean的加載和實例化是有順序的,並且近似一致,固然僅是近似。this
Spring在初始化容器時,會先解析和加載全部的Bean Class,若是符合要求則經過Class生成BeanDefinition,存入BeanFactory中,在加載完全部Bean Class後,開始有序的經過BeanDefinition實例化Bean。spa
咱們先看加載Bean Class過程,零配置下Spring Bean的加載起始於ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry)方法,以下是其加載解析Bean Class的流程:code
配置類能夠是Spring容器的起始配置類,也能夠是經過@ComponentScan掃描獲得的類,也能夠是經過@Import引入的類。若是這個類上含有@Configuration,@Component,@ComponentScan,@Import,@ImportResource註解中的一個,或者內部含有@Bean標識的方法,那麼這個類就是一個配置類,Spring就會按照必定流程去解析這個類上的信息。cdn
在解析的第一步會校驗當前類是否已經被解析過了,若是是,那麼須要按照必定的規則處理(@ComponentScan獲得的Bean能覆蓋@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方法。
順序總結:@ComponentScan > @Import > @Bean
下面看實際的啓動流程:
Bean Class的結構圖如上所示,A是配置類的入口,經過A能直接或間接的引入一個模塊。
此時啓動Spring容器,將A引入容器內。
若是A是經過@ComponentScan掃描到的,那麼此時的加載順序是:
A > D > F > B > E > G > C
若是A是經過@Import形式引入的,那麼此時的加載順訊是:
D > F > B > E > G > A > C
固然以上僅僅表明着加載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自動爲咱們處理好的。
瞭解Spring Bean的解析,加載及實例化的順序機制可以加深對Spring的理解,搭建更優雅簡介的Spring框架。