前言
這篇文章,我打算對我認(rèn)知的ioc容器做一個總結(jié)。方便大家也來了解一下ioc容器,以便于激起大家求知的欲望而自己去翻閱ioc容器的源碼。如果有錯誤的地方,也請不吝指正,共同進(jìn)步。
ioc容器何時被創(chuàng)建
1、第一種方式,容器啟動
容器啟動的話,我們都知道容器中web.xml的載體是ServletContext,web.xml中可以配置listener來監(jiān)聽容器的啟動。如果我們想使用spring,那么就需要配置ContextLoaderListener,這是spring啟動的入口,容器啟動的時候,觸發(fā)listener-class的創(chuàng)建,調(diào)用contextInitialized()方法,在ContextLoaderListener的contextInitialized()方法總最終會調(diào)用AbstractApplicationContext.refresh(),開始容器的創(chuàng)建。
2、第二種方式,springboot啟動
springboot啟動稍微簡單一點(diǎn),啟動的入門在啟動類的run方法里,run方法里會調(diào)用refreshContext(context)進(jìn)行容器的啟動,最終還是調(diào)用AbstractApplicationContext.refresh()。
擴(kuò)展點(diǎn)
ApplicationContextInitializer
在spring初始化應(yīng)用上下文ApplicationContext的時候,可以實(shí)現(xiàn)ApplicationContextInitializer接口,來對應(yīng)用上下文進(jìn)行擴(kuò)展。例如添加一個beanFactoryPostProcessor。這是spring的第一個擴(kuò)展點(diǎn)。例如:
@Slf4j
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
FirstBeanDefinitionRegistryPostProcessor processor = new FirstBeanDefinitionRegistryPostProcessor();
applicationContext.addBeanFactoryPostProcessor(processor);
log.info("自定義BeanDefinitionRegistryPostProcessor已加載進(jìn)spring上下文");
}
}
# 在yml或者propeties配置一下
context:
initializer:
classes: com.yameng.spring.beanFactoryPostPrecessors.MyApplicationContextInitializer
這樣就可以在上下文初始化的時候,把我們自定義的FirstBeanDefinitionRegistryPostProcessor加載進(jìn)beanFactoryPostPrecessors緩存。
這里有個問題。在初始化的時候,spring是怎么找到我們自定義的MyApplicationContextInitializer并執(zhí)行initialize()方法的。答案就是,其實(shí)spring在配置上下文環(huán)境的時候,會去所有的propertyResolver中尋找key為context.initializer.classes的value,這個context.initializer.classes就是我們在配置文件中配置的key。
spring提供的所有的擴(kuò)展點(diǎn),幾乎都是這么實(shí)現(xiàn)的(總結(jié)下來就是,你擴(kuò)展的類實(shí)現(xiàn)了擴(kuò)展的接口,要么以固定的key配置在配置文件里,要么帶上spring的官方注解被掃描加載到beanDefinitionMaps緩存里,然后spring根據(jù)接口的類型去beanDefinitionMaps輪詢獲?。?br>
我們前文這個擴(kuò)展的例子,因?yàn)樯舷挛某跏蓟臅r候,BeanFactory還沒有創(chuàng)建,所以我們只能采用第一種方式,也就是配置在配置文件的形式。
ImportBeanDefinitionRegistrar
@Slf4j
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
log.info("MyImportBeanDefinitionRegistrar.registerBeanDefinitions is run");
}
}
@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)
public class DemoConfig {
}
這也是ioc容器里一個比較重要的拓展點(diǎn), 在spring的bean上,使用@Import注解注冊一個ImportBeanDefinitionRegistrar,就可以在registerBeanDefinitions方法里注冊一些自己的bean定義。我一般是用來給某些bean注冊一些依賴的類和屬性。
看到這里,需要弄明白一個問題:ImportBeanDefinitionRegistrar的加載原理,什么時候被調(diào)用。
首先,MyImportBeanDefinitionRegistrar是依賴于DemoConfig,是在DemoConfig被注冊到beanDefinitionMaps的時候被調(diào)用的,DemoConfig被掃描到是因?yàn)橛蠤Configuration注解。所以一個大概流程就是:
當(dāng)掃描@Configuration注解的時候,掃描到了DemoConfig,然后把DemoConfig封裝成ConfigurationClass,然后發(fā)現(xiàn)這個類有@Impot注解,拿到@impot注解里的類,發(fā)現(xiàn)是ImportBeanDefinitionRegistrar類型,放到ConfigurationClass的importBeanDefinitionRegistrars屬性里,這是一個map,然后遍歷調(diào)用registerBeanDefinitions()方法。
我沒有講的很仔細(xì),你需要自己弄明白,什么時候去掃描@Configuration注解,哪個類干的這個活,在ioc的哪個流程里。自己擼一擼源碼就明白了,后面有時間我會內(nèi)容補(bǔ)充進(jìn)來。
后續(xù)我會做一個ioc容器的流程圖,哪些擴(kuò)展點(diǎn)在哪個環(huán)節(jié)。