SpringBoot源碼分析之---SpringBoot項目啟動類SpringApplication淺析

源碼版本說明

本文源碼采用版本為SpringBoot 2.1.0BUILD,對應(yīng)的SpringFramework 5.1.0.RC1

注意:本文只是從整體上梳理流程巍杈,不做具體深入分析

SpringBoot入口類

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

這是我們?nèi)粘J褂胹pringboot開發(fā)見到次數(shù)最多的引導(dǎo)類了屡拨,完成這個類的編寫萨醒,就完成了一個springboot項目的框架昔脯,springboot就回自動為我們完成一些默認配置,并幫我們初始化上下文容器刀荒,但細節(jié)我們是不知道的明肮,下面我們就一起探索下SpringApplication.run(DemoApplication .class, args);這行代碼背后的故事!

SpringApplication初始化階段

public SpringApplication(Class<?>... primarySources) {
    this(null, primarySources);
}

// 初始化準備階段
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 參數(shù)初始化
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 推斷應(yīng)用類型
    this.webApplicationType = deduceWebApplicationType();
    // 加載ApplicationContextInitializer系列初始化器(從spring.factories文件加載簸州,并實例化和排序后存到this.initializers)
    setInitializers((Collection) getSpringFactoriesInstances(
            ApplicationContextInitializer.class));
    // 加載ApplicationListener系列監(jiān)聽器(從spring.factories文件加載鉴竭,并實例化和排序后存到this.listeners)
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 推斷應(yīng)用入口類(main函數(shù)所在類)
    this.mainApplicationClass = deduceMainApplicationClass();
}

推斷應(yīng)用類型

// 推斷應(yīng)用類型
this.webApplicationType = deduceWebApplicationType();
private WebApplicationType deduceWebApplicationType() {
    if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
            && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)
            && !ClassUtils.isPresent(JERSEY_WEB_ENVIRONMENT_CLASS, null)) {
        return WebApplicationType.REACTIVE;
    }
    for (String className : WEB_ENVIRONMENT_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;
        }
    }
    return WebApplicationType.SERVLET;
}
private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
        + "web.reactive.DispatcherHandler";

private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
        + "web.servlet.DispatcherServlet";

private static final String JERSEY_WEB_ENVIRONMENT_CLASS = "org.glassfish.jersey.server.ResourceConfig";

private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
        "org.springframework.web.context.ConfigurableWebApplicationContext" };

根據(jù)當(dāng)前應(yīng)用ClassPath下是否存在相關(guān)類,來確定應(yīng)用類型岸浑。

加載ApplicationContextInitializer系列初始化器

// 加載ApplicationContextInitializer系列初始化器(從spring.factories文件加載搏存,并實例化和排序后存到this.initializers)
setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class));
public void setInitializers(
        Collection<? extends ApplicationContextInitializer<?>> initializers) {
    this.initializers = new ArrayList<>();
    this.initializers.addAll(initializers);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
        Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // Use names and ensure unique to protect against duplicates
    Set<String> names = new LinkedHashSet<>(
            SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
            classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}
@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<?> instanceClass = ClassUtils.forName(name, classLoader);
            Assert.isAssignable(type, instanceClass);
            Constructor<?> constructor = instanceClass
                    .getDeclaredConstructor(parameterTypes);
            T instance = (T) BeanUtils.instantiateClass(constructor, args);
            instances.add(instance);
        }
        catch (Throwable ex) {
            throw new IllegalArgumentException(
                    "Cannot instantiate " + type + " : " + name, ex);
        }
    }
    return instances;
}

利用Spring工廠加載機制,實例化ApplicationContextInitializer接口的實現(xiàn)類矢洲,被加載的實現(xiàn)類都配置在MATE-INF/spring.factories文件中璧眠,getSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes, Object... args)這個方法就負責(zé)加載配置類并實例化和排序后返回,后面監(jiān)聽器、異常收集器和Runner等也是通過這個類實現(xiàn)實例化對應(yīng)實現(xiàn)類的责静。下面是spring-boot-autoconfigure\src\main\resources\META-INF\spring.factories文件的配置內(nèi)容袁滥。

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer

# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider

加載ApplicationListener系列監(jiān)聽器

// 加載ApplicationListener系列監(jiān)聽器(從spring.factories文件加載,并實例化和排序后存到this.listeners)
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
    this.listeners = new ArrayList<>();
    this.listeners.addAll(listeners);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return getSpringFactoriesInstances(type, new Class<?>[] {});
}

如上面的初始化器灾螃,ApplicationListener系列監(jiān)聽器也是通過getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args)這個方法完成加載题翻、排序及實例化的,而后存入到this.listeners中腰鬼。

推斷應(yīng)用入口類

// 推斷應(yīng)用入口類(main函數(shù)所在類)
this.mainApplicationClass = deduceMainApplicationClass();
private Class<?> deduceMainApplicationClass() {
    try {
        // 通過new一個運行時異常獲取堆棧信息
        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
        for (StackTraceElement stackTraceElement : stackTrace) {
            // 找到main函數(shù)所在的入口類
            if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    }
    catch (ClassNotFoundException ex) {
        // Swallow and continue
    }
    return null;
}

推斷應(yīng)用入口類這部分比較有意思嵌赠,他是通過new了一個運行時異常來拿到main線程的堆棧信息,遍歷所有方法找到main方法所在的類垃喊。

運行階段

// 運行階段
public ConfigurableApplicationContext run(String... args) {
    // 初始化容器啟動計時器
    StopWatch stopWatch = new StopWatch();
    // 開始計時
    stopWatch.start();
    // 初始化上下文ConfigurableApplicationContext
    ConfigurableApplicationContext context = null;
    // 初始化異常收集器
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    // 配置系統(tǒng)參數(shù)"java.awt.headless"
    configureHeadlessProperty();
    // 獲取SpringApplicationRunListener系列監(jiān)聽器(從spring.factories文件加載猾普,并實例化和排序)
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 遍歷所有SpringApplicationRunListener系列監(jiān)聽器,廣播ApplicationStartingEvent
    listeners.starting();
    try {
        // 處理args參數(shù)
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);
        // 準備環(huán)境(創(chuàng)建本谜、配置初家、綁定環(huán)境、廣播ApplicationEnvironmentPreparedEvent)
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                applicationArguments);
        configureIgnoreBeanInfo(environment);
        // 打印Banner
        Banner printedBanner = printBanner(environment);
        // 根據(jù)應(yīng)用類型創(chuàng)建上下文
        context = createApplicationContext();
        // 獲取SpringBootExceptionReporter系列異常收集器(從spring.factories文件加載乌助,并實例化和排序)
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        // 上下文前置處理(執(zhí)行ApplicationContextInitializer系列初始化器溜在、加載資源、廣播ApplicationPreparedEvent)
        prepareContext(context, environment, listeners, applicationArguments,
                printedBanner);
        // 刷新上下文()
        refreshContext(context);
        // 上下文后置處理(目前啥也沒干)
        afterRefresh(context, applicationArguments);
        // 啟動完成他托,打印用時
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
        }
        // 遍歷前面設(shè)置的ConfigurableApplicationContext監(jiān)聽器掖肋,發(fā)布ApplicationStartedEvent
        listeners.started(context);
        // 按順序回調(diào)實現(xiàn)了ApplicationRunner或CommandLineRunner接口的Runners
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        // 處理異常(發(fā)布ExitCodeEvent和ApplicationFailedEvent事件、異常收集器處理異常)
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        // 遍歷前面設(shè)置好的SpringApplicationRunListener監(jiān)聽器赏参,發(fā)布ApplicationReadyEvent
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

獲取SpringApplicationRunListener系列監(jiān)聽器

// 獲取SpringApplicationRunListener系列監(jiān)聽器(從spring.factories文件加載志笼,并實例化和排序)
SpringApplicationRunListeners listeners = getRunListeners(args);
private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
            SpringApplicationRunListener.class, types, this, args));
}

這里我們再一次見到getSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes, Object... args)這個方法,功能同上把篓。

廣播ApplicationStartingEvent事件

// 遍歷所有SpringApplicationRunListener系列監(jiān)聽器纫溃,廣播ApplicationStartingEvent
listeners.starting();
public void starting() {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.starting();
    }
}
public void starting() {
    this.initialMulticaster.multicastEvent(
            new ApplicationStartingEvent(this.application, this.args));
}

這里會遍歷上面拿到的排序好的所有SpringApplicationRunListener系列監(jiān)聽器,廣播ApplicationStartingEvent事件韧掩,這代表Spring應(yīng)用開始啟動紊浩,在這之前只進行了注冊化初始化器和監(jiān)聽器。

準備環(huán)境疗锐、廣播ApplicationEnvironmentPreparedEvent事件

// 準備環(huán)境(創(chuàng)建坊谁、配置、綁定環(huán)境滑臊、廣播ApplicationEnvironmentPreparedEvent)
ConfigurableEnvironment environment = prepareEnvironment(listeners,
        applicationArguments);
private ConfigurableEnvironment prepareEnvironment(
        SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    // Create and configure the environment
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    if (this.webApplicationType == WebApplicationType.NONE) {
        environment = new EnvironmentConverter(getClassLoader())
                .convertToStandardEnvironmentIfNecessary(environment);
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}
public void environmentPrepared(ConfigurableEnvironment environment) {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.environmentPrepared(environment);
    }
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
    this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
            this.application, this.args, environment));
}

這里開始創(chuàng)建口芍、配置和綁定ConfigurableEnvironment環(huán)境,環(huán)境準備好之后開始遍歷SpringApplicationRunListener系列監(jiān)聽器雇卷,廣播ApplicationEnvironmentPreparedEvent事件阶界,代表環(huán)境已準備好虹钮。

開始打印Banner

// 打印Banner
Banner printedBanner = printBanner(environment);
private Banner printBanner(ConfigurableEnvironment environment) {
    // Mode.OFF:不打印banner
    if (this.bannerMode == Banner.Mode.OFF) {
        return null;
    }
    // 加載banner資源聋庵,如果自定義了banner樣式膘融,在這里加載,否則加載默認banner
    ResourceLoader resourceLoader = (this.resourceLoader != null)
            ? this.resourceLoader : new DefaultResourceLoader(getClassLoader());
    // 初始化bannerPrinter
    SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
            resourceLoader, this.banner);
    // Mode.LOG:通過日志打印banner
    if (this.bannerMode == Mode.LOG) {
        return bannerPrinter.print(environment, this.mainApplicationClass, logger);
    }
    // 默認通過控制臺打印banner
    return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}

關(guān)于Banner祭玉,SpringBoot支持關(guān)閉banner打印氧映、打印到log日志和打印到system日志三種方式;同時支持自定義banner脱货,自定義banner又有圖片和txt文本兩種(同時存在時先打印圖片banner岛都,在打印文本banner),圖片banner又支持gif, jpg, png這三種類型的圖片格式banner(git優(yōu)先于jpg優(yōu)先于png)振峻,自定義banner非常簡單臼疫,只需要將banner文件放到classpath:下就好了(resources目錄下),如果存在多個banner文件扣孟,想指定某一個文件烫堤,只需要在application.properties文件加入如下配置就好了,非常方便凤价。

spring.banner.image.location=banner.png
spring.banner.location=banner.txt

根據(jù)應(yīng)用類型創(chuàng)建上下文

// 根據(jù)應(yīng)用類型創(chuàng)建上下文
context = createApplicationContext();
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);
}
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
        + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
        + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
        
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
        + "annotation.AnnotationConfigApplicationContext";

開始創(chuàng)建ConfigurableApplicationContext上下文鸽斟,其中Servlet類型的web應(yīng)用會創(chuàng)建AnnotationConfigServletWebServerApplicationContext類型的上下文,Reactive類型的web應(yīng)用會創(chuàng)建AnnotationConfigReactiveWebServerApplicationContext類型的上下文利诺,非web應(yīng)用會創(chuàng)建AnnotationConfigApplicationContext類型的上下文富蓄。

獲取SpringBootExceptionReporter系列異常收集器

// 獲取SpringBootExceptionReporter系列異常收集器(從spring.factories文件加載,并實例化和排序)
exceptionReporters = getSpringFactoriesInstances(
        SpringBootExceptionReporter.class,
        new Class[] { ConfigurableApplicationContext.class }, context);
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
        Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // Use names and ensure unique to protect against duplicates
    Set<String> names = new LinkedHashSet<>(
            SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
            classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

可以看到慢逾,加載異常收集器與上面初始化器和監(jiān)聽器如出一轍立倍,不做過多闡述。

上下文前置處理侣滩、廣播ApplicationPreparedEvent事件

// 上下文前置處理(執(zhí)行ApplicationContextInitializer系列初始化器口注、加載資源、廣播ApplicationPreparedEvent)
prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
private void prepareContext(ConfigurableApplicationContext context,
        ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments, Banner printedBanner) {
    // 關(guān)聯(lián)上下文和環(huán)境
    context.setEnvironment(environment);
    //
    postProcessApplicationContext(context);
    // 去重并排序前面獲取好的ApplicationContextInitializer初始化器胜卤,執(zhí)行初始化
    applyInitializers(context);
    // 遍歷前面設(shè)置好的SpringApplicationRunListener疆导,但并沒有發(fā)布(目前什么都沒做,貌似為了以后擴展)
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }

    // Add boot specific singleton beans
    // 添加啟動特定的單例bean
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }

    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory) beanFactory)
                .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    // Load the sources
    // 加載sources資源
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");

    load(context, sources.toArray(new Object[0]));
    // 廣播ApplicationPreparedEvent
    listeners.contextLoaded(context);
}

在啟動上下文之前葛躏,會調(diào)用前面初始化好的ApplicationContextInitializer接口實現(xiàn)類澈段,當(dāng)然也包含我們自定義的,所以我們可以自定義初始化器舰攒,在上下文啟動前做一些操作败富。之后會廣播ApplicationPreparedEvent事件,通知SpringApplicationRunListener監(jiān)聽器ConfigurableApplicationContext上下文已準備好(框架中用listeners.contextLoaded(context);方法廣播了ApplicationPreparedEvent事件摩窃,而ApplicationLoadedEvent事件并沒有發(fā)布兽叮,感覺這里以后還會變動芬骄。。鹦聪。)账阻。

刷新上下文

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

發(fā)布ApplicationStartedEvent事件

// 遍歷前面設(shè)置的ConfigurableApplicationContext監(jiān)聽器,發(fā)布ApplicationStartedEvent
listeners.started(context);
public void started(ConfigurableApplicationContext context) {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.started(context);
    }
}
@Override
public void started(ConfigurableApplicationContext context) {
    context.publishEvent(
            new ApplicationStartedEvent(this.application, this.args, context));
}

發(fā)布ApplicationStartedEvent事件泽本,通知 SpringApplicationRunListener系列監(jiān)聽器ConfigurableApplicationContext上下文已啟動完成淘太,Spring Bean 已初始化完成。

回調(diào)實現(xiàn)了ApplicationRunnerCommandLineRunner接口的Runners

// 按順序回調(diào)實現(xiàn)了ApplicationRunner或CommandLineRunner接口的Runners
callRunners(context, applicationArguments);
private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<>();
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    AnnotationAwareOrderComparator.sort(runners);
    for (Object runner : new LinkedHashSet<>(runners)) {
        if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
        }
    }
}

這里是有順序的规丽,ApplicationRunner的實現(xiàn)類優(yōu)先于CommandLineRunner的實現(xiàn)類被回調(diào)

發(fā)布ApplicationReadyEvent事件

try {
    // 遍歷前面設(shè)置好的SpringApplicationRunListener監(jiān)聽器蒲牧,發(fā)布ApplicationReadyEvent
    listeners.running(context);
}
public void running(ConfigurableApplicationContext context) {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.running(context);
    }
}
@Override
public void running(ConfigurableApplicationContext context) {
    context.publishEvent(
            new ApplicationReadyEvent(this.application, this.args, context));
}

發(fā)布ApplicationReadyEvent事件,通知SpringApplicationRunListener系列監(jiān)聽器ConfigurableApplicationContext上下文已經(jīng)在運行,整個容器已經(jīng)準備好冰抢。

發(fā)布ExitCodeEventApplicationFailedEvent事件和異常收集器收集異常信息

catch (Throwable ex) {
    handleRunFailure(context, ex, exceptionReporters, listeners);
    throw new IllegalStateException(ex);
}
private void handleRunFailure(ConfigurableApplicationContext context,
        Throwable exception,
        Collection<SpringBootExceptionReporter> exceptionReporters,
        SpringApplicationRunListeners listeners) {
    try {
        try {
            handleExitCode(context, exception);
            if (listeners != null) {
                listeners.failed(context, exception);
            }
        }
        finally {
            reportFailure(exceptionReporters, exception);
            if (context != null) {
                context.close();
            }
        }
    }
    catch (Exception ex) {
        logger.warn("Unable to close ApplicationContext", ex);
    }
    ReflectionUtils.rethrowRuntimeException(exception);
}

如果啟動過程中出現(xiàn)異常可柿,springboot將會發(fā)布ExitCodeEvent事件通知上下文停止或重啟忆畅,并發(fā)布ApplicationFailedEvent事件通知SpringApplicationRunListener系列監(jiān)聽器钉鸯,最后SpringBootExceptionReporter異常收集器收集打印異常。

總結(jié)

SpringBoot啟動過程大致脈絡(luò)

  • 準備階段
    • 參數(shù)初始化
    • 推斷應(yīng)用類型
    • 加載ApplicationContextInitializer系列初始化器
    • 加載ApplicationListener系列監(jiān)聽器
    • 推斷應(yīng)用入口類(main函數(shù)所在類)
  • 運行階段
    • 初始化容器啟動計時器燥狰,開始計時
    • 初始化上下文ConfigurableApplicationContext、異常收集器
    • 配置系統(tǒng)參數(shù)"java.awt.headless"
    • 獲取SpringApplicationRunListener系列監(jiān)聽器
    • 遍歷所有SpringApplicationRunListener系列監(jiān)聽器煞抬,廣播ApplicationStartingEvent
    • 處理args參數(shù)
    • 準備環(huán)境(創(chuàng)建革答、配置碟嘴、綁定環(huán)境边败、廣播ApplicationEnvironmentPreparedEvent)
    • 配置忽略Bean信息
    • 打印Banner
    • 根據(jù)應(yīng)用類型創(chuàng)建上下文
    • 獲取SpringBootExceptionReporter系列異常收集器
    • 上下文前置處理(執(zhí)行ApplicationContextInitializer系列初始化器排截、加載資源智政、廣播ApplicationPreparedEvent)
    • 刷新上下文
    • 啟動完成,打印用時
    • 遍歷前面設(shè)置的ConfigurableApplicationContext監(jiān)聽器页慷,發(fā)布ApplicationStartedEvent
    • 回調(diào)實現(xiàn)了ApplicationRunner或CommandLineRunner接口的Runners
    • 遍歷前面設(shè)置好的SpringApplicationRunListener監(jiān)聽器酒繁,發(fā)布ApplicationReadyEvent

至此彰居,整個SpringBoot項目已經(jīng)啟動完成,我們可以看到溶握,整個過程中Spring的事件驅(qū)動機制起著舉足輕重的作用杯缺,有了這個機制我們可以知曉容器的啟動過程,并且可以監(jiān)聽到某些事件睡榆,對容器中我們關(guān)心的實例做進一步處理萍肆,我們深入理解事件驅(qū)動機制很有必要,它將幫助我們更好的理解和使用這個Spring框架體系胀屿。如果想要文中中文版SpringBoot注釋源碼塘揣,可以在我的github下載,如果發(fā)現(xiàn)哪里寫的不對宿崭,煩請留言通知我亲铡。

更多信息可以關(guān)注我的個人博客:逸竹小站

也歡迎關(guān)注我的公眾號:yizhuxiaozhan,二維碼:
公眾號二維碼
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末劳曹,一起剝皮案震驚了整個濱河市奴愉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌铁孵,老刑警劉巖锭硼,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蜕劝,居然都是意外死亡檀头,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門岖沛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來暑始,“玉大人,你說我怎么就攤上這事婴削±染担” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵唉俗,是天一觀的道長嗤朴。 經(jīng)常有香客問我配椭,道長,這世上最難降的妖魔是什么雹姊? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任股缸,我火速辦了婚禮,結(jié)果婚禮上吱雏,老公的妹妹穿的比我還像新娘敦姻。我一直安慰自己,他們只是感情好歧杏,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布镰惦。 她就那樣靜靜地躺著,像睡著了一般得滤。 火紅的嫁衣襯著肌膚如雪陨献。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天懂更,我揣著相機與錄音,去河邊找鬼急膀。 笑死沮协,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的卓嫂。 我是一名探鬼主播慷暂,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼晨雳!你這毒婦竟也來了行瑞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤餐禁,失蹤者是張志新(化名)和其女友劉穎血久,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體帮非,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡氧吐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了末盔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筑舅。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖陨舱,靈堂內(nèi)的尸體忽然破棺而出翠拣,到底是詐尸還是另有隱情,我是刑警寧澤游盲,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布误墓,位于F島的核電站蛮粮,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏优烧。R本人自食惡果不足惜蝉揍,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望畦娄。 院中可真熱鬧又沾,春花似錦、人聲如沸熙卡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽驳癌。三九已至滑燃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間颓鲜,已是汗流浹背表窘。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留甜滨,地道東北人乐严。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像衣摩,于是被迫代替她去往敵國和親昂验。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容

  • SpringBoot的啟動很簡單艾扮,代碼如下: @SpringBootApplicationpublicclassM...
    sherlock_6981閱讀 1,487評論 1 1
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理既琴,服務(wù)發(fā)現(xiàn),斷路器泡嘴,智...
    卡卡羅2017閱讀 134,693評論 18 139
  • 文章較長甫恩,建議先收藏,在較空的時候來看磕诊。 Spring Boot 框架的初衷:快速地啟動Spring應(yīng)用Sprin...
    Drew_Zhong閱讀 2,760評論 3 15
  • 首張信用卡選哪家好?小方經(jīng)常被咨詢這樣的問題莱褒,相信很多人都有這樣的疑問击困。在小方看來,適合新手的信用卡要符合以下幾點...
    智匯魔方閱讀 757評論 0 0
  • 我89年今年30歲,2015年過年認識阅茶,當(dāng)時另一個男生喜歡我想追我蛛枚,撈他一起來我同學(xué)這,第一次認識他與他打...
    夏目達閱讀 189評論 1 0