http://www.javashuo.com/article/p-uvudtich-bm.htmlhtml
在前面的幾篇文章中,咱們看到Environment建立、application配置文件的加載、ApplicationContext實例對象的建立、以及主類加載成爲BeanDefinition。作了這麼多的準備,終於到了核心的部分,也就是ioc容器的刷新。spring
這裏,咱們難免要再次回顧一下SpringAplication的run方法springboot
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容器刷新的入口方法。app
可是在閱讀refreshContext方法以前,咱們得先區分一下ApplicationContext和BeanFactory二者之間的關係。在以前的文章中,咱們並無把兩者進行區分。好比,咱們老是說"把Bean註冊到ApplicationContext容器"。post
在咱們的理解中,容器應該是一個空間的概念,用於存放事物的東西。在spring中,存放的是Bean。而BeanFactory提供了這麼一個空間用於存放Bean,因此BeanFactory纔是Bean所在的主要容器,而不是咱們一直說的ApplicationContext。this
既然ApplicationContext不是容器,那它又是啥呢?咱們稱之爲"上下文"。"上下文"的概念咱們也許不見得那麼熟,可是"場景","場所"這樣的概念咱們應該就比較熟悉了。好比說"拍攝場景","交易場所"等。它們的共同點都是事件發生的地方。因此ApplicationContext正是spring定義的應用程序的事件發生場所,也就是所謂的應用上下文。spa
上面,我瞭解了BeanFactory做爲Bean容器,而ApplicationContext做爲上下文。那麼Bean容器和上下文之間是什麼關係呢?咱們能夠看一個代碼片斷設計
// 通用的應用上下文實現 public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { // 默認BeanFactory的實現 private final DefaultListableBeanFactory beanFactory; // 省略 }
咱們看到BeanFactory是被組合在ApplicationContext當中的,因此它們的其中一種關係就是組合關係。也就是說應用上下文中包含着Bean工廠。code
接着,咱們分別看看ApplicationContext的類圖component
咱們看到ApplicationContext和BeanFactory還存在着繼承關係,這意味着ApplicationContext能夠對外被當作BeanFactory來使用,這也是爲何咱們老是把ApplicationContext當作容器來看的主要緣由,由於對外來看二者是一體的。
結合上面的組合關係,咱們能夠知道對內的話ApplicationContext的BeanFactory相關實現會由內部組合的BeanFactory的實現類來完成具體工做。
到這裏,咱們基本就明白了ApplicationContext和BeanFactory之間的關係有兩種:組合、繼承。
後面咱們將稱呼ApplicationContext爲上下文,而BeanFactory爲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方法
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的主要邏輯。