作者丨低調的JVM
來自丨CSDN
https://blog.csdn.net/qq_27529917/article/details/79329809
在使用Spring時,Bean之間會有些依賴,比如一個Bean A實例化時需要用到Bean B,那么B應該在A之前實例化好。很多時候Spring智能地為我們做好了這些工作,但某些情況下可能不是,比如Springboot的@AutoConfigureAfter注解,手動的指定Bean的實例化順序。
了解Spring內Bean的解析,加載和實例化順序機制有助于我們更好的使用Spring/Springboot,避免手動的去干預Bean的加載過程,搭建更優雅的框架。
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預先定義好的名稱,看如下代碼:
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的加載和實例化是有順序的,而且近似一致,當然僅是近似。
Spring在初始化容器時,會先解析和加載所有的Bean Class,如果符合要求則通過Class生成BeanDefinition,存入BeanFactory中,在加載完所有Bean Class后,開始有序的通過BeanDefinition實例化Bean。
我們先看加載Bean Class過程,零配置下Spring Bean的加載起始于ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry)方法,我總結了下其加載解析Bean Class的流程:
配置類可以是Spring容器的起始配置類,也可以是通過@ComponentScan掃描得到的類,也可以是通過@Import引入的類。如果這個類上含有@Configuration,@Component,@ComponentScan,@Import,@ImportResource注解中的一個,或者內部含有@Bean標識的方法,那么這個類就是一個配置類,Spring就會按照一定流程去解析這個類上的信息。
在解析的第一步會校驗當前類是否已經被解析過了,如果是,那么需要按照一定的規則處理(@ComponentScan得到的Bean能覆蓋@Import得到的Bean,@Bean定義的優先級最高)。
如果未解析過,那么開始解析:
解析內部類,查看內部類是否應該被定義成一個Bean,如果是,遞歸解析。
解析@PropertySource,也就是解析被引入的Properties文件。
解析配置類上是否有@ComponentScan注解,如果有則執行掃描動作,通過掃描得到的Bean Class會被立即解析成BeanDefinition,添加進beanDefinitionNames屬性中。之后查看掃描到的Bean Class是否是一個配置類(大部分情況是,因為標識@Component注解),如果是則遞歸解析這個Bean Class。
解析@Import引入的類,如果這個類是一個配置類,則遞歸解析。
解析@Bean標識的方法,此種形式定義的Bean Class不會被遞歸解析
解析父類上的@ComponentScan,@Import,@Bean,父類不會被再次實例化,因為其子類能夠做父類的工作,不需要額外的Bean了。
在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框架。
編輯:jq
-
spring
+關注
關注
0文章
340瀏覽量
14343
原文標題:Spring解析,加載及實例化Bean的順序(零配置)
文章出處:【微信號:harmonyos_developer,微信公眾號:harmonyos_developer】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論