springboot啓動流程(七)ioc容器refresh過程(上篇)

全部文章

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

 

ApplicationContext和BeanFactory

可是在閱讀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容器,進行區分。

 

上下文組合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的主要邏輯。

相關文章
相關標籤/搜索