天天看點

spring容器初始化過程中出現異常_spring容器什麼時候啟動

大家好,又見面了,我是你們的朋友全棧君。

前言

我們知道,spring 的啟動其實就是容器的啟動,而一般情況下,容器指的其實就是上下文

ApplicationContext

AbstractApplicationContext

作為整個

ApplicationContext

體系中最進階的抽象類,為除了

ComplexWebApplicationContext

SimpleWebApplicationContext

這兩個容器外的全部容器,規定好了

refresh

的整體流程,所有的容器在完成一些自己的初始化配置後,都需要調用該

refresh

方法,依次完成指定内容的初始化。

也就是說,讀懂了

AbstractApplicationContext.refresh()

方法,其實就讀懂了容器的啟動流程:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {

        // ================= 一、上下文的初始化 =================
        // 準備上下文
        prepareRefresh();
        // 通知子類重新整理内部工廠
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 準備bean工廠以便在目前上下文中使用
        prepareBeanFactory(beanFactory);

        try {
            // ================= 二、BeanFactory的初始化 =================
            // 對工廠進行預設後置處理
            postProcessBeanFactory(beanFactory);
            // 使用後置處理器對工廠進行處理
            invokeBeanFactoryPostProcessors(beanFactory);
            // 注冊Bean後置處理器
            registerBeanPostProcessors(beanFactory);

            // ================= 三、事件,Bean及其他配置的初始化 =================
            // 初始化此上下文的消息源
            initMessageSource();
            // 為此上下文初始化事件廣播者
            initApplicationEventMulticaster();
            // 初始化特定上下文子類中的其他特殊bean
            onRefresh();
            // 檢查偵聽器bean并注冊
            registerListeners();
            // 執行個體化所有非懶加載的剩餘單例
            finishBeanFactoryInitialization(beanFactory);
            // 完成重新整理
            finishRefresh();
        }


        // ================= 異常處理 =================
        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
            }
            // 銷毀已建立的單例
            destroyBeans();
            // 重置上下文的激活狀态
            cancelRefresh(ex);
            throw ex;
        }
        finally {
            // 重置内部的一些中繼資料緩存
            resetCommonCaches();
        }
    }
}           

複制

從總體來看,該方法描述的初始化過程大概分為三步:

  • 上下文的初始化;
  • BeanFactory

    初始化;
  • 事件,Bean及其他配置的初始化;

筆者将基于 spring 源碼

5.2.x

分支,分别通過五篇文章從源碼分析 spring 容器的初始化過程。

本文是其中的第三篇文章,将介紹上下文中事件,Bean及其他配置的初始化。

相關文章:

  • 深入了解Spring容器初始化(一):上下文的初始化;
  • 深入了解Spring容器初始化(二):BeanFactory的初始化;
  • 深入了解Spring容器初始化(三):事件及其他配置的初始化;

一、初始化資料源

調用

AbstarctApplicationContext.initMessageSource()

用于初始化上下文所使用的資料源對象

MessageSource

,這個配置用于支援國際化的資訊處理,一般情況下比較少會直接去配置它:

protected void initMessageSource() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 如果目前上下文否存在名為“messageSource”的Bean
    if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
        this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
        // Make MessageSource aware of parent MessageSource.
        if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
            // 若父容器沒有MessageSource,就把它設定為父容器的MessageSource
            HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
            if (hms.getParentMessageSource() == null) {
                // Only set parent context as parent MessageSource if no parent MessageSource
                // registered already.
                hms.setParentMessageSource(getInternalParentMessageSource());
            }
        }
        if (logger.isTraceEnabled()) {
            logger.trace("Using MessageSource [" + this.messageSource + "]");
        }
    }
    else {
        // 不存在就嘗試擷取父容器的MessageSource
        DelegatingMessageSource dms = new DelegatingMessageSource();
        dms.setParentMessageSource(getInternalParentMessageSource());
        this.messageSource = dms;
        beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
        if (logger.isTraceEnabled()) {
            logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
        }
    }
}           

複制

二、初始化事件廣播者

調用

AbstarctApplicationContext.initApplicationEventMulticaster()

是初始化 spring 事件機制的第一步,它的作用很簡單:

如果目前

BeanFactory

有名為

“applicationEventMulticaster”

ApplicationEventMulticaster

,就把它設定為目前上下文的事件廣播器,否則就建立并在

BeanFactory

中注冊一個

SimpleApplicationEventMulticaster

執行個體作為目前上下文的事件廣播器。

protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 是否存在“applicationEventMulticaster”這個Bean
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        // 如果存在就把它設定為目前上下文的事件廣播器
        this.applicationEventMulticaster =
            beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
        if (logger.isTraceEnabled()) {
            logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
        }
    }
    else {
        // 沒有就建立一個SimpleApplicationEventMulticaster作為目前上下文的事件廣播器
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
        if (logger.isTraceEnabled()) {
            logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
                         "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
        }
    }
}           

複制

三、初始化特殊Bean

AbstarctApplicationContext.onRefresh()

用于在完成上下文與

BeanFactory

初始化後去初始化一些特殊的

Bean

,其實從方法名就可以看出來,這個方法主要是作為上下文初步重新整理完畢後的回調使用。

AbstarctApplicationContext

中隻提供了空實作,實際上也隻有很少的實作類會去重新實作這個方法,至少在

5.2.x

裡面,關于這個方法的有用實作隻有:

UiApplicationContextUtils.initThemeSource(this)           

複制

該代碼用于初始化一些 spring 的“主題資源”,一般用于配合消息國際化進行一些處理。

四、注冊事件監聽器

當消息和事件相關的内容都準備就緒後,上下文會調用

AbstarctApplicationContext.registerListeners

方法以注冊事件監聽器

ApplicationListener

這一步代碼不動,實際上邏輯也很簡單:

  • 向事件廣播器注冊已經被注冊的上下文中的監聽器;
  • 向事件廣播器注冊還沒有被執行個體化的監聽器的

    BeanName

  • 釋出一些早期事件;
protected void registerListeners() {
    // 向事件廣播器注冊已經被注冊的上下文中的監聽器
    for (ApplicationListener<?> listener : getApplicationListeners()) {
        getApplicationEventMulticaster().addApplicationListener(listener);
    }

    // 向事件廣播器注冊指定的監聽器,不過這裡隻注冊BeanName,
    // 因為有些監聽器Bean是由FactoryBean生産的,而在這裡FactoryBean實際上還沒被生成出來
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String listenerBeanName : listenerBeanNames) {
        getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }

    // 釋出一些早期事件
    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
        for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
            getApplicationEventMulticaster().multicastEvent(earlyEvent);
        }
    }
}           

複制

這裡有兩個比較有意思的地方:

  • getApplicationListeners

    擷取的監聽器實際上也是通過一個名為

    EventListenerMethodProcessor

    BeanFactoryPostProcessor

    注冊到上下文的;
  • 注冊

    BeanName

    而不是直接注冊

    Bean

    這一點是為了遷就

    FactoryBean

    。實際上在初始化

    BeanFactory

    的時候,調用

    BeanFactoryPostProcessor

    和注冊

    BeanPostProcessor

    也都專門對此進行了處理;

五、執行個體化工廠中的Bean

當調用

AbstarctApplicationContext.finishBeanFactoryInitialization()

的時候,spring 會根據

BeanFactory

中已經注冊的

BeanDefinition

執行個體化所有非懶加載的單例

Bean

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // 為BeanFactory設定ConversionService
    // 該接口為spring轉換器體系的入口
    if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
        beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
        beanFactory.setConversionService(
            beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
    }

    // 注冊一個StringValueResolver,沒有就從上下文的環境對象中擷取
    // 該解析器用于解析配置檔案中的一些占位符以及SpEL表達式
    if (!beanFactory.hasEmbeddedValueResolver()) {
        beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
    }

    // 若存在AOP使用的支援類加載時織入切面邏輯的類加載器,則優先将該Bean初始化
    String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
    for (String weaverAwareName : weaverAwareNames) {
        getBean(weaverAwareName);
    }

    // 由于類加載器已經初始化完成,是以可以停用臨時的類加載器了
    beanFactory.setTempClassLoader(null);

    // 鎖定目前工廠的配置
    beanFactory.freezeConfiguration();

    // 初始化剩餘未初始化的非懶加載單例Bean
    beanFactory.preInstantiateSingletons();
}           

複制

這個方法總共幹了五件事:

  • BeanFactory

    設定類型轉換服務

    ConversionService

  • BeanFactory

    設定占位符轉換器

    StringValueResolver

  • 禁用臨時的類加載器,若有則啟用支援類加載時織入切面邏輯的類加載器;
  • 鎖定目前

    BeanFactory

    的配置;
  • 初始化剩餘未初始化的非懶加載單例

    Bean

這裡我們重點關注

BeanFactory.preInstantiateSingletons()

方法,此處是實際上完成

Bean

初始化的代碼:

public void preInstantiateSingletons() throws BeansException {
    if (logger.isTraceEnabled()) {
        logger.trace("Pre-instantiating singletons in " + this);
    }

    // Iterate over a copy to allow for init methods which in turn register new bean definitions.
    // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

    // 周遊beanName,若BeanName是可以執行個體化的非懶加載單例Bean,則将其執行個體化
    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
			// 如果是FactoryBean
            if (isFactoryBean(beanName)) {
                Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                if (bean instanceof FactoryBean) {
                    FactoryBean<?> factory = (FactoryBean<?>) bean;
                    boolean isEagerInit;
                    // 類型為SmartFactoryBean,則是否立刻執行個體化由SmartFactoryBean.isEagerInit()決定
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged(
                            (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
                            getAccessControlContext());
                    }
                    else {
                    // 類型不為SmartFactoryBean,則不立刻執行個體化
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                       ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    if (isEagerInit) {
                        getBean(beanName);
                    }
                }
            }
            else {
                // 執行個體化bean
                getBean(beanName);
            }
        }
    }

    // 擷取所有實作了SmartInitializingSingleton接口的Bean,調用Bean初始化後回調afterSingletonsInstantiated
    for (String beanName : beanNames) {
        Object singletonInstance = getSingleton(beanName);
        if (singletonInstance instanceof SmartInitializingSingleton) {
            SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                    smartSingleton.afterSingletonsInstantiated();
                    return null;
                }, getAccessControlContext());
            }
            else {
                smartSingleton.afterSingletonsInstantiated();
            }
        }
    }
}           

複制

這一步主要幹了兩件事:

  • 如果是允許執行個體化的非懶加載普通

    Bean

    ,就直接初始化;
  • 如果是允許執行個體化的非懶加載

    FactoryBean

    ,則判斷它是否是

    SmartFactoryBean

    1. 如果不是,則放棄直接初始化;
    2. 如果是,則根據

      SmartFactoryBean.isEagerInit()

      判斷是否要直接初始化;
  • 初始化所有可初始化的

    Bean

    後,如果這些

    Bean

    實作了

    SmartInitializingSingleton

    接口,則調用該接口提供的回調函數;

這裡需要注意兩點:

  • BeanFactory.getBean()

    實際才是最終完成

    BeanFactory

    建立

    Bean

    執行個體操作的方法,在這個方法中将根據

    BeanDefinition

    完成各自依賴的自動裝配、

    Bean

    的後置處理等操作,三級緩存也是在這個時候使用的,這部分的内容将會在後續另起一篇文章分析;
  • 此處僅預加載了

    FactoryBean

    ,而沒有懶加載

    FactoryBean

    裡面的

    Bean

    ,是以

    FactoryBean

    提供的

    Bean

    總是懶加載的;
  • SmartInitializingSingleton

    接口用于提供

    BeanFactory

    在初始化全部非懶加載

    Bean

    時調用的回調函數;

至此,

BeanFactory

中所有可以預先初始化的

Bean

都完成的初始化,我們已經可以通過

BeanFactory

正常的去擷取

Bean

了。

六、完成重新整理

AbstractApplicationContext.finishRefresh()

是完成容器重新整理的最後一步,它跟

AbstractApplicationContext.onRefresh()

一樣是一個鈎子方法。

protected void finishRefresh() {
    // 清空資源緩存
    clearResourceCaches();

    // 初始化上下文的生命周期處理器
    initLifecycleProcessor();

    // 調用上下文的生命周期處理器
    getLifecycleProcessor().onRefresh();

    // 釋出上下文重新整理完畢事件
    publishEvent(new ContextRefreshedEvent(this));

    // 注冊用于支援通過JMX管理spring的元件,這裡不過多分析,
    // 關于JMX具體可以參考這篇文章:https://www.wdbyte.com/java/jmx.html#_3-2-%E8%B5%84%E6%BA%90%E4%BB%A3%E7%90%86-mbean-server
    LiveBeansView.registerApplicationContext(this);
}           

複制

1、清空上下文資源緩存

public void clearResourceCaches() {
    this.resourceCaches.clear();
}           

複制

2、初始化上下文的生命周期處理器

protected void initLifecycleProcessor() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 若存在名為“lifecycleProcessor”的bean,則設定為生命周期處理器
    if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
        this.lifecycleProcessor =
            beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
        if (logger.isTraceEnabled()) {
            logger.trace("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
        }
    }
    // 若不存在名為“lifecycleProcessor”的bean,則建立一個DefaultLifecycleProcessor并設定為生命周期處理器
    else {
        DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor();
        defaultProcessor.setBeanFactory(beanFactory);
        this.lifecycleProcessor = defaultProcessor;
        beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor);
        if (logger.isTraceEnabled()) {
            logger.trace("No '" + LIFECYCLE_PROCESSOR_BEAN_NAME + "' bean, using " +
                         "[" + this.lifecycleProcessor.getClass().getSimpleName() + "]");
        }
    }
}           

複制

3、生命周期處理

這裡需要着重研究一下生命周期處理器的調用。

getLifecycleProcessor().onRefresh()

這一步,将會擷取上一步設定到上下文中的

LifecycleProcessor

然後調用:

// AbstraceApplicationContext.getLifecycleProcessor()
LifecycleProcessor getLifecycleProcessor() throws IllegalStateException {
    if (this.lifecycleProcessor == null) {
        throw new IllegalStateException("LifecycleProcessor not initialized - " +
                                        "call 'refresh' before invoking lifecycle methods via the context: " + this);
    }
    return this.lifecycleProcessor;
}           

複制

這裡我們以預設的生命周期處理器

DefaultLifecycleProcessor

為例:

@Override
public void onRefresh() {
    startBeans(true);
    this.running = true;
}

private void startBeans(boolean autoStartupOnly) {
    // 擷取所有實作了Lifecycle接口的Bean,并按階段分組裝到不同的LifecycleGroup裡
    Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
    Map<Integer, LifecycleGroup> phases = new HashMap<>();
    lifecycleBeans.forEach((beanName, bean) -> {
        // 同時滿足下述條件的Bean不會被處理
        // 1.入參的autoStartupOnly為true
        // 2.bean實作了SmartLifecycle接口
        // 3.SmartLifecycle.isAutoStartup()方法傳回false
        if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
            // 若實作了SmartLifecycle接口,則傳回SmartLifecycle.getPhase(),否則預設傳回0
            int phase = getPhase(bean); 
            LifecycleGroup group = phases.get(phase);
            if (group == null) {
                group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly);
                phases.put(phase, group);
            }
            group.add(beanName, bean);
        }
    });
    
    // 按階段從小到大排序,依次處理
    if (!phases.isEmpty()) {
        List<Integer> keys = new ArrayList<>(phases.keySet());
        Collections.sort(keys);
        for (Integer key : keys) {
            phases.get(key).start();
        }
    }
}           

複制

可以看到,這裡針對

SmartLifecycle

接口的實作類做了很多特殊化的處理,預設情況下:

  • 實作了

    SmartLifecycle

    接口的

    Bean

    ,需要保證

    SmartLifecycle.isAutoStartup

    傳回

    true

    才會被處理;
  • 沒實作

    SmartLifecycle

    接口,但是實作了

    Lifecycle

    接口的

    Bean

    會被直接處理;

并且,在處理

Bean

的時候,還會根據聲明周期“階段”按順序從小到大排序:

  • 實作了

    SmartLifecycle

    接口的

    Bean

    ,按照

    SmartLifecycle.getPhase

    傳回值排序從小到大執行;
  • 沒實作

    SmartLifecycle

    接口,但是實作了

    Lifecycle

    接口的

    Bean

    ,“階段”視為 0,會被最先處理;

4、釋出上下文重新整理完畢事件

這個操作其實也很簡單,其實就是調用時間廣播器推送一個

ContextRefreshedEvent

事件:

public class ContextRefreshedEvent extends ApplicationContextEvent {
    public ContextRefreshedEvent(ApplicationContext source) {
        super(source);
    }
}           

複制

這個事件裡唯一一個參數就是上下文本身。

這一部分主要邏輯在事件推送上,後續會在專門的文章分析 spring 提供的事件機制,這裡就不過多展開。

總結

本文内容比較零散,主要幹三件事:

  • 初始化消息源相關元件:
    1. initMessageSource

      :初始化上下文使用的消息源;
    2. onRefresh

      :上下文重新整理時的回調函數,但是一般隻用于加載

      ThemeSource

  • 初始化事件相關元件:
    1. initApplicationEventMulticaster

      :初始化事件廣播器;
    2. registerListeners

      :注冊容器中的事件監聽器

      ApplicationListener

  • 初始化

    BeanFactory

    中所有非抽象的非懶加載

    Bean

  • 完成重新整理:
    1. 清空上下文中的資源緩存;
    2. 初始化并調用

      Bean

      生命周期處理器;
    3. 釋出上下文重新整理時間;
    4. 注冊并初始化用于支援 JMX 的元件;

釋出者:全棧程式員棧長,轉載請注明出處:https://javaforall.cn/170760.html原文連結:https://javaforall.cn