SpringBoot啟動流程源碼分析

一、源碼分析

首先是項目啟動類

public static void main(String[] args) {
   SpringApplication.run(MarsApplication.class, args);
}

初始化時,會加載META-INF/spring.factories文件,來看一下deduceWebEnvironment()方法

private WebApplicationType deduceWebApplicationType() {
    if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
            && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
        return WebApplicationType.REACTIVE;
    }
    for (String className : WEB_ENVIRONMENT_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;
        }
    }
    return WebApplicationType.SERVLET;
}

這里主要是通過判斷REACTIVE相關(guān)的字節(jié)碼是否存在,如果不存在,則web環(huán)境即為SERVLET類型。這里設(shè)置好web環(huán)境類型,在后面會根據(jù)類型初始化對應(yīng)環(huán)境。ApplicationContextInitializer是spring組件spring-context組件中的一個接口,主要是spring ioc容器刷新之前的一個回調(diào)接口,用于處于自定義邏輯。spring.factories文件中的實現(xiàn)類:

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

還有1個為:org.springframework.boot.autoconfigure.BackgroundPreinitializer這10個監(jiān)聽器會貫穿SpringBoot整個生命周期。

來看一下run方法:

public ConfigurableApplicationContext run(String... args) {
    //時間監(jiān)控
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    //java.awt.headless是J2SE的一種模式用于在缺少顯示屏、鍵盤或者鼠標(biāo)時的系統(tǒng)配置,很多監(jiān)控工具如jconsole 需要將該值設(shè)置為true,系統(tǒng)變量默認(rèn)為true
    configureHeadlessProperty();
    //獲取spring.factories中的監(jiān)聽器變量,args為指定的參數(shù)數(shù)組,默認(rèn)為當(dāng)前類SpringApplication
    //第一步:獲取并啟動監(jiān)聽器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);
        //第二步:構(gòu)造容器環(huán)境
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                applicationArguments);
        //設(shè)置需要忽略的bean
        configureIgnoreBeanInfo(environment);
        //打印banner
        Banner printedBanner = printBanner(environment);
        //第三步:創(chuàng)建容器
        context = createApplicationContext();
        //第四步:實例化SpringBootExceptionReporter.class,用來支持報告關(guān)于啟動的錯誤
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        //第五步:準(zhǔn)備容器
        prepareContext(context, environment, listeners, applicationArguments,
                printedBanner);
        //第六步:刷新容器
        refreshContext(context);
        //第七步:刷新容器后的擴(kuò)展接口
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}
  • 第一步:獲取并啟動監(jiān)聽器
  • 第二步:構(gòu)造容器環(huán)境
  • 第三步:創(chuàng)建容器
  • 第四步:實例化SpringBootExceptionReporter.class,用來支持報告關(guān)于啟動的錯誤
  • 第五步:準(zhǔn)備容器
  • 第六步:刷新容器
  • 第七步:刷新容器后的擴(kuò)展接口

二、步驟分析

2.1 獲取并啟動監(jiān)聽器
2.1.1 獲取監(jiān)聽器

SpringApplicationRunListeners listeners = getRunListeners(args); 跟進(jìn)getRunListeners方法:

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
            SpringApplicationRunListener.class, types, this, args));
}

上面可以看到,args本身默認(rèn)為空,但是在獲取監(jiān)聽器的方法中,getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)將當(dāng)前對象作為參數(shù),該方法用來獲取spring.factories對應(yīng)的監(jiān)聽器:

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

整個SpringBoot 框架中獲取factories的方式統(tǒng)一如下:

@SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
        Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
        Set<String> names) {
    List<T> instances = new ArrayList<>(names.size());
    for (String name : names) {
        try {
            //裝載class文件到內(nèi)存
            Class<?> instanceClass = ClassUtils.forName(name, classLoader);
            Assert.isAssignable(type, instanceClass);
            Constructor<?> constructor = instanceClass
                    .getDeclaredConstructor(parameterTypes);
            //主要通過反射創(chuàng)建實例
            T instance = (T) BeanUtils.instantiateClass(constructor, args);
            instances.add(instance);
        }
        catch (Throwable ex) {
            throw new IllegalArgumentException(
                    "Cannot instantiate " + type + " : " + name, ex);
        }
    }
    return instances;
}

上面通過反射獲取實例時會觸發(fā)EventPublishingRunListener的構(gòu)造函數(shù):

public EventPublishingRunListener(SpringApplication application, String[] args) {
    this.application = application;
    this.args = args;
    this.initialMulticaster = new SimpleApplicationEventMulticaster();
    for (ApplicationListener<?> listener : application.getListeners()) {
        this.initialMulticaster.addApplicationListener(listener);
    }
}

重點來看一下addApplicationListener方法:

@Override
public void addApplicationListener(ApplicationListener<?> listener) {
    synchronized (this.retrievalMutex) {
        // Explicitly remove target for a proxy, if registered already,
        // in order to avoid double invocations of the same listener.
        Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
        if (singletonTarget instanceof ApplicationListener) {
            this.defaultRetriever.applicationListeners.remove(singletonTarget);
        }
        //內(nèi)部類對象
        this.defaultRetriever.applicationListeners.add(listener);
        this.retrieverCache.clear();
    }
}

上述方法定義在SimpleApplicationEventMulticaster父類AbstractApplicationEventMulticaster中。關(guān)鍵代碼為this.defaultRetriever.applicationListeners.add(listener);,這是一個內(nèi)部類,用來保存所有的監(jiān)聽器。也就是在這一步,將spring.factories中的監(jiān)聽器傳遞到SimpleApplicationEventMulticaster中。

2.1.2 啟動監(jiān)聽器

listeners.starting();,獲取的監(jiān)聽器為EventPublishingRunListener,從名字可以看出是啟動事件發(fā)布監(jiān)聽器,主要用來發(fā)布啟動事件。

@Override
public void starting() {
//關(guān)鍵代碼,這里是創(chuàng)建application啟動事件`ApplicationStartingEvent`
    this.initialMulticaster.multicastEvent(
            new ApplicationStartingEvent(this.application, this.args));
}

EventPublishingRunListener這個是SpringBoot框架中最早執(zhí)行的監(jiān)聽器,在該監(jiān)聽器執(zhí)行started()方法時,會繼續(xù)發(fā)布事件,也就是事件傳遞。這種實現(xiàn)主要還是基于Spring的事件機(jī)制。繼續(xù)跟進(jìn)SimpleApplicationEventMulticaster,有個核心方法:

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        //獲取線程池,如果為空則同步處理。這里線程池為空,還未沒初始化。
        Executor executor = getTaskExecutor();
        if (executor != null) {
            //異步發(fā)送事件
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            //同步發(fā)送事件
            invokeListener(listener, event);
        }
    }
}

這里會根據(jù)事件類型ApplicationStartingEvent獲取對應(yīng)的監(jiān)聽器,在容器啟動之后執(zhí)行響應(yīng)的動作。

這是SpringBoot啟動過程中,第一處根據(jù)類型,執(zhí)行監(jiān)聽器的地方。根據(jù)發(fā)布的事件類型從上述10種監(jiān)聽器中選擇對應(yīng)的監(jiān)聽器進(jìn)行事件發(fā)布,當(dāng)然如果繼承了 SpringCloud或者別的框架,就不止10個了。這里選了一個 SpringBoot 的日志監(jiān)聽器來進(jìn)行講解,核心代碼如下:

@Override
public void onApplicationEvent(ApplicationEvent event) {
    //在springboot啟動的時候
    if (event instanceof ApplicationStartedEvent) {
        onApplicationStartedEvent((ApplicationStartedEvent) event);
    }
    //springboot的Environment環(huán)境準(zhǔn)備完成的時候
    else if (event instanceof ApplicationEnvironmentPreparedEvent) {
        onApplicationEnvironmentPreparedEvent(
                (ApplicationEnvironmentPreparedEvent) event);
    }
    //在springboot容器的環(huán)境設(shè)置完成以后
    else if (event instanceof ApplicationPreparedEvent) {
        onApplicationPreparedEvent((ApplicationPreparedEvent) event);
    }
    //容器關(guān)閉的時候
    else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event)
            .getApplicationContext().getParent() == null) {
        onContextClosedEvent();
    }
    //容器啟動失敗的時候
    else if (event instanceof ApplicationFailedEvent) {
        onApplicationFailedEvent();
    }
}

因為我們的事件類型為ApplicationEvent,所以會執(zhí)行onApplicationStartedEvent((ApplicationStartedEvent) event);。SpringBoot會在運(yùn)行過程中的不同階段,發(fā)送各種事件,來執(zhí)行對應(yīng)監(jiān)聽器的對應(yīng)方法。

2.2 環(huán)境構(gòu)建

ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments); 跟進(jìn)去該方法:

private ConfigurableEnvironment prepareEnvironment(
        SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    // Create and configure the environment
    //獲取對應(yīng)的ConfigurableEnvironment
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    //配置
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    //發(fā)布環(huán)境已準(zhǔn)備事件,這是第二次發(fā)布事件
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    if (this.webApplicationType == WebApplicationType.NONE) {
        environment = new EnvironmentConverter(getClassLoader())
                .convertToStandardEnvironmentIfNecessary(environment);
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}

來看一下getOrCreateEnvironment()方法,前面已經(jīng)提到,environment已經(jīng)被設(shè)置了servlet類型,所以這里創(chuàng)建的是環(huán)境對象是StandardServletEnvironment。

private ConfigurableEnvironment getOrCreateEnvironment() {
    if (this.environment != null) {
        return this.environment;
    }
    if (this.webApplicationType == WebApplicationType.SERVLET) {
        return new StandardServletEnvironment();
    }
    return new StandardEnvironment();
}

枚舉類WebApplicationType是SpringBoot2新增的特性,主要針對spring5引入的reactive特性。枚舉類型如下:

public enum WebApplicationType {
    //不需要再web容器的環(huán)境下運(yùn)行,普通項目
    NONE,
    //基于servlet的web項目
    SERVLET,
    //這個是spring5版本開始的新特性
    REACTIVE
}

Environment接口提供了4種實現(xiàn)方式,StandardEnvironment、StandardServletEnvironment和MockEnvironment、StandardReactiveWebEnvironment,分別代表普通程序、Web程序、測試程序的環(huán)境、響應(yīng)式web環(huán)境。
在返回return new StandardServletEnvironment();對象的時候,會完成一系列初始化動作,主要就是將運(yùn)行機(jī)器的系統(tǒng)變量和環(huán)境變量,加入到其父類AbstractEnvironment定義的對象MutablePropertySources中,MutablePropertySources對象中定義了一個屬性集合:

private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<PropertySource<?>>();

執(zhí)行到這里,系統(tǒng)變量和環(huán)境變量已經(jīng)被載入到配置文件的集合中,接下來就行解析項目中的配置文件。來看一下listeners.environmentPrepared(environment);,上面已經(jīng)提到了,這里是第二次發(fā)布事件。什么事件呢?顧名思義,系統(tǒng)環(huán)境初始化完成的事件。

可以看到獲取到的監(jiān)聽器和第一次發(fā)布啟動事件獲取的監(jiān)聽器有幾個是重復(fù)的,這也驗證了監(jiān)聽器是可以多次獲取,根據(jù)事件類型來區(qū)分具體處理邏輯。上面介紹日志監(jiān)聽器的時候已經(jīng)提到。主要來看一下ConfigFileApplicationListener,該監(jiān)聽器非常核心,主要用來處理項目配置。項目中的 properties 和yml文件都是其內(nèi)部類所加載。

首先還是會去讀spring.factories 文件,List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();獲取的處理類有以下四種:

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=  //一個@FunctionalInterface函數(shù)式接口
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,//為springCloud提供的擴(kuò)展類
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,//支持json環(huán)境變量
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor //springBoo2提供的一個包裝類,主要將`StandardServletEnvironment`包裝成`SystemEnvironmentPropertySourceEnvironmentPostProcessor`對象

在執(zhí)行完上述三個監(jiān)聽器流程后,ConfigFileApplicationListener會執(zhí)行該類本身的邏輯。由其內(nèi)部類Loader加載項目制定路徑下的配置文件:

private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
2.3 創(chuàng)建容器

context = createApplicationContext(); 繼續(xù)跟進(jìn)該方法:

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch (this.webApplicationType) {
            case SERVLET:
                contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
                break;
            case REACTIVE:
                contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                break;
            default:
                contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
            }
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                    "Unable create a default ApplicationContext, "
                            + "please specify an ApplicationContextClass",
                    ex);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

上面可以看出,這里創(chuàng)建容器的類型 還是根據(jù)webApplicationType進(jìn)行判斷的,因為該類型為SERVLET類型,所以會通過反射裝載對應(yīng)的字節(jié)碼,如下:

public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

該對象是SpringBoot2創(chuàng)建的容器,后續(xù)所有的操作都會基于該容器。

2.4 報告錯誤信息

這里還是以同樣的方式獲取spring.factories文件中的指定類:

exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);

該類主要是在項目啟動失敗之后,打印log:

private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters,Throwable failure) {
    try {
        for (SpringBootExceptionReporter reporter : exceptionReporters) {
            if (reporter.reportException(failure)) {
                //上報錯誤log
                registerLoggedException(failure);
                return;
            }
        }
    }
    catch (Throwable ex) {
        // Continue with normal handling of the original failure
    }
    if (logger.isErrorEnabled()) {
        logger.error("Application run failed", failure);
        registerLoggedException(failure);
    }
}
2.5 準(zhǔn)備容器

這一步主要是在容器刷新之前的準(zhǔn)備動作。包含一個非常關(guān)鍵的操作:將啟動類注入容器,為后續(xù)開啟自動化配置奠定基礎(chǔ)。

prepareContext(context, environment, listeners, applicationArguments,printedBanner);

繼續(xù)跟進(jìn)該方法:

private void prepareContext(ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments, Banner printedBanner) {
    //設(shè)置容器環(huán)境,包括各種變量
    context.setEnvironment(environment);
    //執(zhí)行容器后置處理
    postProcessApplicationContext(context);
    //執(zhí)行容器中的ApplicationContextInitializer(包括 spring.factories和自定義的實例)
    applyInitializers(context);
    //發(fā)送容器已經(jīng)準(zhǔn)備好的事件,通知各監(jiān)聽器
    listeners.contextPrepared(context);
    //打印log
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    //注冊啟動參數(shù)bean,這里將容器指定的參數(shù)封裝成bean,注入容器
    context.getBeanFactory().registerSingleton("springApplicationArguments",
            applicationArguments);
    //設(shè)置banner
    if (printedBanner != null) {
        context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
    }
    // Load the sources
    //獲取我們的啟動類指定的參數(shù),可以是多個
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    //加載我們的啟動類,將啟動類注入容器
    load(context, sources.toArray(new Object[0]));
    //發(fā)布容器已加載事件。
    listeners.contextLoaded(context);
}

來看一下上面的幾個核心處理

2.5.1 容器的后置處理
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
    if (this.beanNameGenerator != null) {
        context.getBeanFactory().registerSingleton(
                AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
                this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
        if (context instanceof GenericApplicationContext) {
            ((GenericApplicationContext) context)
                    .setResourceLoader(this.resourceLoader);
        }
        if (context instanceof DefaultResourceLoader) {
            ((DefaultResourceLoader) context)
                    .setClassLoader(this.resourceLoader.getClassLoader());
        }
    }
}

這里默認(rèn)不執(zhí)行任何邏輯,因為beanNameGenerator和resourceLoader默認(rèn)為空。之所以這樣做,是SpringBoot留給我們的擴(kuò)展處理方式,類似于這樣的擴(kuò)展,Spring中也有很多。

2.5.2 加載啟動指定類(重點)

這里會將我們的啟動類加載Spring容器beanDefinitionMap中,為后續(xù)SpringBoot自動化配置奠定基礎(chǔ),SpringBoot為我們提供的各種注解配置也與此有關(guān)。

load(context, sources.toArray(new Object[0]));
protected void load(ApplicationContext context, Object[] sources) {
    if (logger.isDebugEnabled()) {
        logger.debug(
                "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
    }
    BeanDefinitionLoader loader = createBeanDefinitionLoader(
            getBeanDefinitionRegistry(context), sources);
    if (this.beanNameGenerator != null) {
        loader.setBeanNameGenerator(this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
        loader.setResourceLoader(this.resourceLoader);
    }
    if (this.environment != null) {
        loader.setEnvironment(this.environment);
    }
    loader.load();
}

這里參數(shù)即為我們項目啟動時傳遞的參數(shù):SpringApplication.run(SpringBootApplication.class, args);由于我們指定了啟動類,所以上面也就是加載啟動類到容器。需要注意的是,SpringBoot2會優(yōu)先選擇groovy加載方式,找不到再選用java方式?;蛟Sgroovy動態(tài)加載class文件的性能更勝一籌。

private int load(Class<?> source) {
    if (isGroovyPresent()
            && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
        // Any GroovyLoaders added in beans{} DSL can contribute beans here
        GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
                GroovyBeanDefinitionSource.class);
        load(loader);
    }
    if (isComponent(source)) {
        //以注解的方式,將啟動類bean信息存入beanDefinitionMap
        this.annotatedReader.register(source);
        return 1;
    }
    return 0;
}

上面代碼中啟動類被加載到 beanDefinitionMap中,后續(xù)該啟動類將作為開啟自動化配置的入口。

2.5.3 通知監(jiān)聽器,容器已準(zhǔn)備就緒
listeners.contextLoaded(context);

主還是針對一些日志等監(jiān)聽器的響應(yīng)處理。

2.6 刷新容器

執(zhí)行到這里,SpringBoot相關(guān)的處理工作已經(jīng)結(jié)束,接下的工作就交給了Spring。

synchronized (this.startupShutdownMonitor) {
    // Prepare this context for refreshing.
    /**
     * 刷新上下文環(huán)境
     * 初始化上下文環(huán)境,對系統(tǒng)的環(huán)境變量或者系統(tǒng)屬性進(jìn)行準(zhǔn)備和校驗
     * 如環(huán)境變量中必須設(shè)置某個值才能運(yùn)行,否則不能運(yùn)行,這個時候可以在這里加這個校驗,
     * 重寫initPropertySources方法就好了
     */
    prepareRefresh();

    // Tell the subclass to refresh the internal bean factory.
    /**
     * 初始化BeanFactory,解析XML,相當(dāng)于之前的XmlBeanFactory的操作,
     */
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    // Prepare the bean factory for use in this context.
    /**
     * 為上下文準(zhǔn)備BeanFactory,即對BeanFactory的各種功能進(jìn)行填充,如常用的注解@Autowired @Qualifier等
     * 設(shè)置SPEL表達(dá)式#{key}的解析器
     * 設(shè)置資源編輯注冊器,如PerpertyEditorSupper的支持
     * 添加ApplicationContextAwareProcessor處理器
     * 在依賴注入忽略實現(xiàn)*Aware的接口,如EnvironmentAware、ApplicationEventPublisherAware等
     * 注冊依賴,如一個bean的屬性中含有ApplicationEventPublisher(beanFactory),則會將beanFactory的實例注入進(jìn)去
     */
    prepareBeanFactory(beanFactory);

    try {
        // Allows post-processing of the bean factory in context subclasses.
        /**
         * 提供子類覆蓋的額外處理,即子類處理自定義的BeanFactoryPostProcess
         */
        postProcessBeanFactory(beanFactory);

        // Invoke factory processors registered as beans in the context.
        /**
         * 激活各種BeanFactory處理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor
         * 執(zhí)行對應(yīng)的postProcessBeanDefinitionRegistry方法 和  postProcessBeanFactory方法
         */
        invokeBeanFactoryPostProcessors(beanFactory);

        // Register bean processors that intercept bean creation.
        /**
         * 注冊攔截Bean創(chuàng)建的Bean處理器,即注冊BeanPostProcessor,不是BeanFactoryPostProcessor,注意兩者的區(qū)別
         * 注意,這里僅僅是注冊,并不會執(zhí)行對應(yīng)的方法,將在bean的實例化時執(zhí)行對應(yīng)的方法
         */
        registerBeanPostProcessors(beanFactory);

        // Initialize message source for this context.
        /**
         * 初始化上下文中的資源文件,如國際化文件的處理等
         */
        initMessageSource();

        // Initialize event multicaster for this context.
        /**
         * 初始化上下文事件廣播器,并放入applicatioEventMulticaster,如ApplicationEventPublisher
         */
        initApplicationEventMulticaster();

        // Initialize other special beans in specific context subclasses.
        /**
         * 給子類擴(kuò)展初始化其他Bean
         */
        onRefresh();

        // Check for listener beans and register them.
        /**
         * 在所有bean中查找listener bean,然后注冊到廣播器中
         */
        registerListeners();

        // Instantiate all remaining (non-lazy-init) singletons.
        /**
         * 設(shè)置轉(zhuǎn)換器
         * 注冊一個默認(rèn)的屬性值解析器
         * 凍結(jié)所有的bean定義,說明注冊的bean定義將不能被修改或進(jìn)一步的處理
         * 初始化剩余的非惰性的bean,即初始化非延遲加載的bean
         */
        finishBeanFactoryInitialization(beanFactory);

        // Last step: publish corresponding event.
        /**
         * 初始化生命周期處理器DefaultLifecycleProcessor,DefaultLifecycleProcessor含有start方法和stop方法,spring啟動的時候調(diào)用start方法開始生命周期,
         * spring關(guān)閉的時候調(diào)用stop方法來結(jié)束生命周期,通常用來配置后臺程序,啟動有一直運(yùn)行,如一直輪詢kafka
         * 啟動所有實現(xiàn)了Lifecycle接口的類
         * 通過spring的事件發(fā)布機(jī)制發(fā)布ContextRefreshedEvent事件,以保證對應(yīng)的監(jiān)聽器做進(jìn)一步的處理,即對那種在spring啟動后需要處理的一些類,這些類實現(xiàn)了
         * ApplicationListener<ContextRefreshedEvent> ,這里就是要觸發(fā)這些類的執(zhí)行(執(zhí)行onApplicationEvent方法)另外,spring的內(nèi)置Event有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent
         * 完成初始化,通知生命周期處理器lifeCycleProcessor刷新過程,同時發(fā)出ContextRefreshEvent通知其他人
         */
        finishRefresh();
    }

    catch (BeansException ex) {
        if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                    "cancelling refresh attempt: " + ex);
        }

        // Destroy already created singletons to avoid dangling resources.
        destroyBeans();
        // Reset 'active' flag.
        cancelRefresh(ex);
        // Propagate exception to caller.
        throw ex;
    }

    finally {
        // Reset common introspection caches in Spring's core, since we
        // might not ever need metadata for singleton beans anymore...
        resetCommonCaches();
    }
}

refresh方法在Spring整個源碼體系中舉足輕重,是實現(xiàn)IOC和AOP的關(guān)鍵。

2.7 刷新容器后的擴(kuò)展接口
protected void afterRefresh(ConfigurableApplicationContext context,
        ApplicationArguments args) {
}

擴(kuò)展接口,設(shè)計模式中的模板方法,默認(rèn)為空實現(xiàn)。如果有自定義需求,可以重寫該方法。比如打印一些啟動結(jié)束log,或者一些其它后置處理。

轉(zhuǎn)載自:SpringBoot | 第一篇:啟動流程源碼分析(上)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容