SpringBoot 啟動(dòng)過程

本文基于SpringBoot 2.1.12.RELEASE

一個(gè)常見的SpringBoot啟動(dòng)類為

@SpringBootApplication
public class Application {

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

}

其中 @SpringBootApplication@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三者的聚合

其中@SpringBootConfiguration@Configuration無異, ComponentScan也不用多說,@EnableAutoConfiguation則實(shí)際上用了@Import({AutoConfigurationImportSelector.class}),用于將指定目錄下的配置類進(jìn)行加載,其原理類似SPI,也無須多講。

1. SpringApplication#run()方法

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class<?>[] { primarySource }, args);
}
// 上一級(jí)調(diào)用的接口
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return new SpringApplication(primarySources).run(args);
    }

可以看到,run()方法最后返回的是 new SpringApplication(primarySources).run(args)

2. SpringApplication 的構(gòu)造函數(shù)

接下來,進(jìn)入此處的SpringApplication的構(gòu)造函數(shù),到最底層調(diào)用的是

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        // 1.將配置類放入
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // 2.判斷webApplication的類型,枚舉有  NONE  SERVLET REACTIVE
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        
        // 3.加載ApplicationContextInitializer配置并實(shí)例化,關(guān)聯(lián)到SpringApplication#initializer 
      //底層是SpringFactoriesLoader.loadFactoryNames(type, classLoader)
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 4.與上一句類似 加載ApplicationListener配置并實(shí)例化,關(guān)聯(lián)到SpringApplication#listener
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // 5.推斷應(yīng)用引導(dǎo)類
        this.mainApplicationClass = deduceMainApplicationClass();
}

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));中具體的調(diào)用路徑為

// 加載制定目錄下對應(yīng)類型的類,并初始化后返回實(shí)例
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = getClassLoader();
        // 作用是獲取META-INF/spring.factories下的所有ApplicationContextInitializer類
        Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // 初始化對應(yīng)的類
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        // 排序
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
}

// 將所有的ApplicationContextInitializer 關(guān)聯(lián)到SpringApplication#initializers中
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
        this.initializers = new ArrayList<>();
        this.initializers.addAll(initializers);
}

this.mainApplicationClass = deduceMainApplicationClass();的具體函數(shù)為

// 推斷引導(dǎo)類
private Class<?> deduceMainApplicationClass() {
        try {
            // 獲取當(dāng)前線程執(zhí)行棧
            StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
            for (StackTraceElement stackTraceElement : stackTrace) {
                // 哪個(gè)類包含main方法
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        }
        catch (ClassNotFoundException ex) {
            // Swallow and continue
        }
        return null;
    }

3. SpringApplication(primarySources).run方法—— 真正啟動(dòng)

// 創(chuàng)建并refresh一個(gè)ApplicationContext
public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        //設(shè)置系統(tǒng)屬性『java.awt.headless』,為true則啟用headless模式支持
        configureHeadlessProperty();
        //通過*SpringFactoriesLoader*檢索*META-INF/spring.factories*,
       //找到聲明的所有SpringApplicationRunListener的實(shí)現(xiàn)類并將其實(shí)例化,
       //之后逐個(gè)調(diào)用其started()方法,廣播SpringBoot要開始執(zhí)行了
        SpringApplicationRunListeners listeners = getRunListeners(args);
        //發(fā)布應(yīng)用開始啟動(dòng)事件
        listeners.starting();
        try {
        //初始化參數(shù)
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            //創(chuàng)建并配置當(dāng)前SpringBoot應(yīng)用將要使用的Environment(包括配置要使用的PropertySource以及Profile),
        //并遍歷調(diào)用所有的SpringApplicationRunListener的environmentPrepared()方法,廣播Environment準(zhǔn)備完畢。
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            configureIgnoreBeanInfo(environment);
            //打印banner
            Banner printedBanner = printBanner(environment);
            //創(chuàng)建應(yīng)用上下文
            context = createApplicationContext();
            //通過*SpringFactoriesLoader*檢索*META-INF/spring.factories*,獲取并實(shí)例化異常分析器
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            //為ApplicationContext加載environment,之后逐個(gè)執(zhí)行ApplicationContextInitializer的initialize()方法來進(jìn)一步封裝ApplicationContext,
        //并調(diào)用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一個(gè)空的contextPrepared()方法】,
        //之后初始化IoC容器,并調(diào)用SpringApplicationRunListener的contextLoaded()方法,廣播ApplicationContext的IoC加載完成,
        //這里就包括通過**@EnableAutoConfiguration**導(dǎo)入的各種自動(dòng)配置類。
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            //刷新上下文
            refreshContext(context);
            //再一次刷新上下文,其實(shí)是空方法,可能是為了后續(xù)擴(kuò)展。
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            //發(fā)布應(yīng)用已經(jīng)啟動(dòng)的事件
            listeners.started(context);
            //遍歷所有注冊的ApplicationRunner和CommandLineRunner,并執(zhí)行其run()方法。
        //我們可以實(shí)現(xiàn)自己的ApplicationRunner或者CommandLineRunner,來對SpringBoot的啟動(dòng)過程進(jìn)行擴(kuò)展。
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

        try {
        //應(yīng)用已經(jīng)啟動(dòng)完成的監(jiān)聽事件
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

}

其中,SpringApplicationRunListeners listeners = getRunListeners(args);getRunListeners中調(diào)用了getSpringFactorieInstances方法,其作用就是加載制定目錄下的配置類


private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    // 獲取配置的SpringApplicationRunListener
        return new SpringApplicationRunListeners(logger,
                getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
3.1 創(chuàng)建上下文

context = createApplicationContext()創(chuàng)建上下文的語句,其源碼為

/**
    默認(rèn)上下文
     */
    public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
            + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

    /**
    reactive上下文
     */
    public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
            + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_SERVLET_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);
    }

即在默認(rèn)web環(huán)境下,上下文的實(shí)現(xiàn)為AnnotationConfigServletWebServerApplicationContext

3.2 上下文運(yùn)行前準(zhǔn)備

prepareContext(context, environment, listeners, applicationArguments, printedBanner);的源碼為

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        // 設(shè)置環(huán)境 必須在refresh之前 原因?yàn)槿羰褂?AbstractApplicationContext#createEnvironment,則由BeanFactoryPostProcessor實(shí)現(xiàn)environment屬性的裝載,但是無法保證在PostProcessor中最早執(zhí)行
        context.setEnvironment(environment);
        // 上下文后置處理  處理resourceLoader,classLoader以及conversionService
        postProcessApplicationContext(context);
        // 迭代執(zhí)行所有的ApplicationContextInitializer的實(shí)現(xiàn)類
        applyInitializers(context);
        // 發(fā)布 contextPrepared的事件
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
        // Add boot specific singleton beans
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof DefaultListableBeanFactory) {
            //允許Override
            ((DefaultListableBeanFactory) beanFactory)
                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        // 讀取所有的源信息 參照第一步的 primarySources
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        // 將所有的上下文配置源(sources)讀取并放入上下文中
        load(context, sources.toArray(new Object[0]));
        // 執(zhí)行SpringApplicationRunListener#contextLoaded回調(diào)
        // 發(fā)布ApplicationLoadedEvent
        listeners.contextLoaded(context);
    }

可以看到,在此期間內(nèi),listeners發(fā)送了兩次事件,一次是contextPrepared,一次是contextLoaded

3.3 refreshContext

內(nèi)部為

private void refreshContext(ConfigurableApplicationContext context) {
        refresh(context);
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            }
            catch (AccessControlException ex) {
                // Not allowed in some environments.
            }
        }
    }

其實(shí)就是調(diào)用的((AbstractApplicationContext) applicationContext).refresh()

至此,我們基本上把SpringBoot的啟動(dòng)的大致過程摸清楚了。

在查詢資料的過程中,發(fā)現(xiàn)一篇文章寫的不錯(cuò),是側(cè)重于啟動(dòng)過程中Tomcat的處理,有興趣可以看看

Tomcat在SpringBoot中是如何啟動(dòng)的

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

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