所有文章
https://www.cnblogs.com/lay2017/p/11478237.html
正文
在前面的幾篇文章中,我們看到Environment創建、application配置文件的加載、ApplicationContext實例對象的創建、以及主類加載成為BeanDefinition。做了這么多的准備,終於到了核心的部分,也就是ioc容器的刷新。
這里,我們不免要再次回顧一下SpringAplication的run方法
public ConfigurableApplicationContext run(String... args) { // 聲明一個Context容器 ConfigurableApplicationContext context = null; // 獲取監聽器 SpringApplicationRunListeners listeners = getRunListeners(args); // 調用監聽器的啟動 listeners.starting(); try { // 創建並配置Environment(這個過程會加載application配置文件) ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // 根據應用類型創建對應的Context容器 context = createApplicationContext(); // 刷新Context容器之前的准備 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 刷新Context容器 refreshContext(context); // 刷新Context容器之后處理 afterRefresh(context, applicationArguments); // Context容器refresh完畢發布 listeners.started(context); // 觸發Context容器refresh完以后的執行 callRunners(context, applicationArguments); } catch (Throwable ex) {} try { // Context啟動完畢,Runner運行完畢發布 listeners.running(context); } catch (Throwable ex) {} return context; }
run方法中,prepareContext和afterRefresh之間的refreshContext方法正是ioc容器刷新的入口方法。
ApplicationContext和BeanFactory
但是在閱讀refreshContext方法之前,我們得先區分一下ApplicationContext和BeanFactory兩者之間的關系。在之前的文章中,我們並沒有把二者進行區分。比如,我們總是說"把Bean注冊到ApplicationContext容器"。
在我們的理解中,容器應該是一個空間的概念,用於存放事物的東西。在spring中,存放的是Bean。而BeanFactory提供了這么一個空間用於存放Bean,所以BeanFactory才是Bean所在的主要容器,而不是我們一直說的ApplicationContext。
既然ApplicationContext不是容器,那它又是啥呢?我們稱之為"上下文"。"上下文"的概念我們也許不見得那么熟,但是"場景","場所"這樣的概念我們應該就比較熟悉了。比如說"拍攝場景","交易場所"等。它們的共同點都是事件發生的地方。所以ApplicationContext正是spring定義的應用程序的事件發生場所,也就是所謂的應用上下文。
上面,我了解了BeanFactory作為Bean容器,而ApplicationContext作為上下文。那么Bean容器和上下文之間是什么關系呢?我們可以看一個代碼片段
// 通用的應用上下文實現 public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { // 默認BeanFactory的實現 private final DefaultListableBeanFactory beanFactory; // 省略 }
我們看到BeanFactory是被組合在ApplicationContext當中的,所以它們的其中一種關系就是組合關系。也就是說應用上下文中包含着Bean工廠。
接着,我們分別看看ApplicationContext的類圖
我們看到ApplicationContext和BeanFactory還存在着繼承關系,這意味着ApplicationContext可以對外被當做BeanFactory來使用,這也是為什么我們總是把ApplicationContext當做容器來看的主要原因,因為對外來看兩者是一體的。
結合上面的組合關系,我們可以知道對內的話ApplicationContext的BeanFactory相關實現會由內部組合的BeanFactory的實現類來完成具體工作。
到這里,我們基本就明白了ApplicationContext和BeanFactory之間的關系有兩種:組合、繼承。
后面我們將稱呼ApplicationContext為上下文,而BeanFactory為Bean容器,進行區分。
上下文組合Bean工廠
那么BeanFactory是什么時候被組合到ApplicationContext當中的呢?我們先看看ApplicationContext的實現類的類圖
注意!springboot默認的servlet項目的ApplicationContext實現類是AnnotationCofnigServletWebServerApplicationContext,所以我們會以它作為實現類來看
我們自下而上,順着繼承鏈找到GenericApplicationContext我們就會看到之前出現過的代碼片段
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { private final DefaultListableBeanFactory beanFactory; public GenericApplicationContext() { this.beanFactory = new DefaultListableBeanFactory(); } // 省略 }
這里,在GenericApplicationContext的構造方法當中構建了一個DefaultListableBeanFactory的實例對象。DefaultListableBeanFactory也就是BeanFactory的默認實現,那么也就是說在構造ApplicationContext實例對象的時候創建並組合了一個BeanFactory的實例。
我們順便也看看DefaultListableBeanFactory的繼承關系吧
這個類圖只保留了BeanFactory的東西,設計路線自BeanFactory到DefaultListableBeanFactory也很清晰。
refreshContext刷新過程
下面,我們將正式進行refreshContext方法的閱讀。打開refreshContext方法
private void refreshContext(ConfigurableApplicationContext context) { refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } }
跟進refresh方法
protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext) applicationContext).refresh(); }
我們看到,這里調用的是AbstractApplicationContext的refresh方法,順序AnnotationConfigServletWebServerApplicationContext的繼承鏈向上可以找到AbstractApplicationContext。
我們繼續跟進AbstractApplicationContext的refresh方法,refresh方法有點長
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 刷新前准備,設置flag、時間,初始化properties等 prepareRefresh(); // 獲取ApplicationContext中組合的BeanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 設置類加載器,添加后置處理器等准備 prepareBeanFactory(beanFactory); try { // 供子類實現的后置處理 postProcessBeanFactory(beanFactory); // 調用Bean工廠的后置處理器 invokeBeanFactoryPostProcessors(beanFactory); // 注冊Bean的后置處理器 registerBeanPostProcessors(beanFactory); // 初始化消息源 initMessageSource(); // 初始化事件廣播 initApplicationEventMulticaster(); // 供之類實現的,初始化特殊的Bean onRefresh(); // 注冊監聽器 registerListeners(); // 實例化所有的(non-lazy-init)單例Bean finishBeanFactoryInitialization(beanFactory); // 發布刷新完畢事件 finishRefresh(); } catch (BeansException ex) { // } finally { // } } }
在上一篇文章中,我們提到了這么一個初始化過程:
annotation或者xml中Bean的配置 --> 內存中的BeanDefinition --> Bean
也就是實際的配置,轉化成內存中的配置對象,再根據配置對象轉化成具體的實例對象。說白了就是從元數據到實例的一個轉化過程。
為什么會提及這么一個轉化過程呢?因為我們的refresh過程主要包含的就是其中的一步,也就是從annotation或者xml的Bean配置 --> 內存中的BeanDefinition的過程。這個過程的實現在調用Bean工廠的后置處理器的時候完成,也就是invokeBeanFactoryPostProcessors方法
我們跟進invokeBeanFactoryPostProcessors方法
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // }
這里先獲取了所有后置處理器,然后調用處理。再跟進PostProcessorRegistrationDelegate的invokeBeanFactoryFactoryPostProcessors方法
該方法很長,我們刪減掉大部分內容
public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors ) { // if (beanFactory instanceof BeanDefinitionRegistry) { // while (reiterate) { // 調用BeanDefinition注冊的后置處理器 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); // } // } else { // } // }
可以看到,調用后置處理器的時候會調用到注冊BeanDefinition的后置處理器。也就是從這里開始作為BeanDefinition的注冊入口
跟進invokeBeanDefinitionRegistryPostProcessors
private static void invokeBeanDefinitionRegistryPostProcessors( Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry ) { for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) { postProcessor.postProcessBeanDefinitionRegistry(registry); } }
這里我們需要斷點一下,看看有哪些后置處理器處理BeanDefinition注冊
我們看到了ConfigurationClassPostProcessor也就是它完成BeanDefinition注冊這項工作的
我們跟進ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { // 省略 processConfigBeanDefinitions(registry); }
繼續跟進,我們看到processConfigBeanDefinitions方法挺長的,進行了大量的縮減
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames(); for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) || ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) { // } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { // 默認僅有主類被添加 configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } // // 解析被 @Configuration 注解的類 ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); // do { // 解析的核心方法 parser.parse(candidates); parser.validate(); // candidates.clear(); // } while (!candidates.isEmpty()); // }
上一篇文章中,也就是在prepareContext方法的核心邏輯里,main方法所在的主類將會被作為BeanDefinition加載到BeanFactory當中。而在這里,該主類將被作為一個配置類被解析,解析器即ConfigurationClassParser。
我們跟進parse方法看看
public void parse(Set<BeanDefinitionHolder> configCandidates) { for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { if (bd instanceof AnnotatedBeanDefinition) { // 主類的解析將從這里進入 parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) { parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName()); } else { parse(bd.getBeanClassName(), holder.getBeanName()); } } catch (BeanDefinitionStoreException ex) {} catch (Throwable ex) {} } this.deferredImportSelectorHandler.process(); }
繼續跟進parse方法
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { processConfigurationClass(new ConfigurationClass(metadata, beanName)); }
可以看到主類作為配置類的解析過程將從processConfigurationClass這里開始
總結
到這里,ioc容器的refresh過程先做一個小結。我們知道了上下文和Bean容器是繼承關系又是組合關系。refreshContext的核心就是為了加載BeanDefinition,而加載BeanDefinition將從main方法所在的主類開始,主類作為一個配置類將由ConfigurationClassParser解析器來完成解析的職責。下一篇文章,我們將會看到從主類中解析出BeanDefinition的主要邏輯。