SpringBoot 啟動流程


SpringBoot啟動流程

整個啟動流程包含,推斷 WEB 應用類型,設置初始化器,設置 ApplicationListener 監聽器,獲取並啟動 SpringApplicationRunListener 類,准備 Spring 環境,創建並執行 banner 打印類,創建應用上下文,准備應用上下文,刷新應用上下文,刷新應用上下文之后的調用,執行所有的 Runner 運行器。

Spring Boot 的入口程序

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

創建 SpringApplication 對象

  • 初始化主要加載資源類到集合中
  • 推斷當前 WEB 應用類型(NONE、SERVLET、REACTIVE)
  • 設置應用上下文初始化器
  • 設置 ApplicationListener 監聽器
  • 推斷主入口應用類
// 創建一個新的實例,這個應用上下文將從指定的來源加載 Bean
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // 初始化資源加載器,默認 null
    this.resourceLoader = resourceLoader;
    // 斷言主資源不能為空
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 初始化主要加載資源類集合並去重
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 推斷當前 WEB 應用類型,NONE、SERVLET、REACTIVE
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 加載引導程序(2.4.0增加的功能)
    this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
    // 設置初始化器
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 設置監聽器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 推斷主入口應用類
    this.mainApplicationClass = deduceMainApplicationClass();
}

run 方法啟動

  • 創建引導上下文
  • 獲取並啟動所有 SpringApplicationRunListener 對象
  • 創建默認的應用參數類
  • 准備 Spring 應用環境
  • 創建並執行 banner 打印類
  • 創建應用上下文
  • 准備應用上下文
  • 刷新應用上下文
public ConfigurableApplicationContext run(String... args) {
    // 創建並啟動計時監控類
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    // 創建引導上下文
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;
    // 設置系統屬性“java.awt.headless”的值,默認為 true
    configureHeadlessProperty();
    // 創建所有 Spring 運行監聽器並發布應用啟動事件,
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    try {
        // 初始化默認應用參數類
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 根據運行監聽器、引導上下文、應用參數來准備 Spring 環境
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        // 將要忽略的 bean 參數打開
        configureIgnoreBeanInfo(environment);
        // 創建並執行 banner 打印類
        Banner printedBanner = printBanner(environment);
        // 創建應用上下文
        context = createApplicationContext();
        // 設置應用程序啟動 步驟
        context.setApplicationStartup(this.applicationStartup);
        // 准備應用上下文
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        // 刷新應用上下文
        refreshContext(context);
        // 刷新之后,空方法
        afterRefresh(context, applicationArguments);
        // 停止計時監控類
        stopWatch.stop();
        // 輸出日志記錄
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        // 發布應用上下文啟動監聽事件
        listeners.started(context);
        // 執行所有的 Runner 運行器
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        // 發布應用上下文就緒事件
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

創建引導上下文

創建並啟動引導上下文,2.4.0 之后的版本增加的新功能

private DefaultBootstrapContext createBootstrapContext() {
    DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
    this.bootstrappers.forEach((initializer) -> initializer.intitialize(bootstrapContext));
    return bootstrapContext;
}

獲取所有 SpringApplicationRunListener 對象並啟動

// 從 META-INF/spring.factories 中獲取所有的配置類,並將其封裝到 SpringApplicationRunListeners 對象中去,主要創建的對象為 EventPublishingRunListener
private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger,  getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);
}
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);
    }
}
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
    doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
                    (step) -> {
                        if (mainApplicationClass != null) {
                            step.tag("mainApplicationClass", mainApplicationClass.getName());
                        }
                    });
}
// 廣播事件
public void starting(ConfigurableBootstrapContext bootstrapContext) {
    this.initialMulticaster
        .multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}
// applicationStartingEvent 是 SpringBoot 框架最早執行的監聽器,在該監聽器執行 started 方法時,會繼續發布事件,主要是基於 Spring 的事件機制
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    Executor executor = getTaskExecutor();
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        // 如果線程池為空,則同步發送事件,否則異步發送事件
        if (executor != null) {
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            invokeListener(listener, event);
        }
    }
}
// 返回與給定事件匹配的監聽器
protected Collection<ApplicationListener<?>> getApplicationListeners(
    ApplicationEvent event, ResolvableType eventType) {

    Object source = event.getSource();
    Class<?> sourceType = (source != null ? source.getClass() : null);
    ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

    // Potential new retriever to populate
    CachedListenerRetriever newRetriever = null;

    // Quick check for existing entry on ConcurrentHashMap
    CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
    if (existingRetriever == null) {
        // Caching a new ListenerRetriever if possible
        if (this.beanClassLoader == null ||
            (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
             (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
            newRetriever = new CachedListenerRetriever();
            existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
            if (existingRetriever != null) {
                newRetriever = null;  // no need to populate it in retrieveApplicationListeners
            }
        }
    }

    if (existingRetriever != null) {
        Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
        if (result != null) {
            return result;
        }
        // If result is null, the existing retriever is not fully populated yet by another thread.
        // Proceed like caching wasn't possible for this current local attempt.
    }

    return retrieveApplicationListeners(eventType, sourceType, newRetriever);
}

創建默認的應用參數類

Source 繼承自 SimpleCommandLinePropertySource,用於解析簡單的命令行參數

public DefaultApplicationArguments(String... args) {
    Assert.notNull(args, "Args must not be null");
    this.source = new Source(args);
    this.args = args;
}

准備 Spring 應用環境

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
                                                   DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
    // Create and configure the environment
    // 創建或者獲取應用環境
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 配置應用環境
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    ConfigurationPropertySources.attach(environment);
    // listeners 環境准備,廣播 ApplicationEnvironmentPreparedEvent
    listeners.environmentPrepared(bootstrapContext, environment);
    // 附加配置源
    DefaultPropertiesPropertySource.moveToEnd(environment);
    // 配置其他激活文件
    configureAdditionalProfiles(environment);
    // 將環境綁定給當前應用程序
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
    }
    // 配置 propertySource 對它自己的遞歸依賴
    ConfigurationPropertySources.attach(environment);
    return environment;
}

創建 banner 打印類並執行

打印 banner 的詳細操作過程

private Banner printBanner(ConfigurableEnvironment environment) {
    if (this.bannerMode == Banner.Mode.OFF) {
        return null;
    }
    ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
        : new DefaultResourceLoader(null);
    SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
    if (this.bannerMode == Mode.LOG) {
        return bannerPrinter.print(environment, this.mainApplicationClass, logger);
    }
    return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}

創建應用上下文

根據不同的應用類型初始化不同的山下文應用類

protected ConfigurableApplicationContext createApplicationContext() {
    return this.applicationContextFactory.create(this.webApplicationType);
}

准備應用上下文

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    // 設置應用上下文的 environment
    context.setEnvironment(environment);
    // 應用山下文的后置處理
    postProcessApplicationContext(context);
    // 為上下文應用所有初始化器,執行前面設置的 ApplicationContextInitializer 類
    applyInitializers(context);
    // 觸發所有 SpringApplicationRunListener 監聽器的 contextPrepared 事件方法。添加所有的事件監聽器
    listeners.contextPrepared(context);
    bootstrapContext.close(context);
    // 記錄啟動日志
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    // 注冊啟動參數bean,將容器指定的參數封裝成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);
    }
    if (this.lazyInitialization) {
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }
    // Load the sources
    // 加載所有資源
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    // 將 bean 加載到上下文中
    load(context, sources.toArray(new Object[0]));
	// 觸發所有 SpringApplicationRunListener 監聽器的 contextLoaded 事件方法
    listeners.contextLoaded(context);
}
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();
}

// 后續會使用其將主資源加載到上下文中去,供后續解析使用
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
    Assert.notNull(registry, "Registry must not be null");
    Assert.notEmpty(sources, "Sources must not be empty");
    this.sources = sources;
    this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
    this.xmlReader = (XML_ENABLED ? new XmlBeanDefinitionReader(registry) : null);
    this.groovyReader = (isGroovyPresent() ? new GroovyBeanDefinitionReader(registry) : null);
    this.scanner = new ClassPathBeanDefinitionScanner(registry);
    this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}
void load() {
    for (Object source : this.sources) {
        load(source);
    }
}

private void load(Object source) {
    Assert.notNull(source, "Source must not be null");
    if (source instanceof Class<?>) {
        // Class 的方式加載
        load((Class<?>) source);
        return;
    }
    if (source instanceof Resource) {
        // Resource 資源的方式加載
        load((Resource) source);
        return;
    }
    if (source instanceof Package) {
        load((Package) source);
        return;
    }
    if (source instanceof CharSequence) {
        load((CharSequence) source);
        return;
    }
    throw new IllegalArgumentException("Invalid source type " + source.getClass());
}

private void 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);
        ((GroovyBeanDefinitionReader) this.groovyReader).beans(loader.getBeans());
    }
    if (isEligible(source)) {
        // 將符合條件的類注冊到應用上下文
        this.annotatedReader.register(source);
    }
}

// 不是常規的閉包並且不是匿名類,即可
private boolean isEligible(Class<?> type) {
    return !(type.isAnonymousClass() || isGroovyClosure(type) || hasNoConstructors(type));
}

刷新應用上下文

最終會調用到 AbstractApplicationContext 抽象類中的 refresh() 方法中去

refreshContext(context);

刷新后應用上下文調用

空方法,用於擴展

執行所有的 Runner 運行器

執行所有 Runner 執行器,執行所有 ApplicationRunner 和 CommandLineRunner 兩種運行器。

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);
        }
    }
}


免責聲明!

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



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