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


所有文章

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的主要邏輯。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM