likes
comments
collection
share

SpringBoot系列(一) SpringBoot启动流程

作者站长头像
站长
· 阅读数 13

Spring Boot是一个基于Spring Framework的快速开发应用程序的框架。它提供了一种简单粗暴的方式来构建应用程序,开发人员只需要少量的配置即可快速的构建可运行的应用程序。然而,即使是Spring Boot的核心框架,也有很多可以让我们深入了解它如何工作的知识点。在这篇文章中,我们将深入研究Spring Boot的启动流程,并提供运行过程中的详细解释。

概述

在Spring Boot应用程序的启动过程中,一个类被认为是Spring Boot的入口点:org.springframework.boot.loader.Launcher。这个类的主要任务是设置ClassLoader和加载应用程序的主类。在执行Launchermain方法之前,Spring Boot会执行一些准备工作,例如创建ClassLoader和加载一些必需的类。接下来,我们将逐步了解Spring Boot在启动过程中的每个步骤。

创建ClassLoader

为了加载应用程序的类和资源,Spring Boot使用了一个自定义的ClassLoader:org.springframework.boot.loader.LaunchedURLClassLoader。Spring Boot使用LaunchedURLClassLoader替代了JDK提供的ClassLoader,这个ClassLoader可以加载来自多个JAR包和目录的类和资源。在Spring Boot启动流程的第一步中,它会执行以下代码创建LaunchedURLClassLoader:

Thread.currentThread().setContextClassLoader(Launcher.class.getClassLoader());
...
List<Archive> archives = new ArrayList<>(this.archives);
...
ClassLoader classLoader = createClassLoader(getClassPathArchivesIterator(archives), this.classLoader);

getClassLoader()方法用于获取Launcher类的ClassLoader,并将其设置为当前线程的ContextClassLoader。这个步骤非常重要,因为Spring Boot应用程序可能需要与其他应用程序或库共享ClassLoader。接下来,Spring Boot将使用getClassPathArchivesIterator()方法从应用程序的JAR包或目录中获取所有的类文件。然后,Spring Boot使用createClassLoader()方法创建LaunchedURLClassLoader

private ClassLoader createClassLoader(Iterator<Archive> archivesIterator, ClassLoader parent) {
    URL[] urls = getUrlsForArchives(archivesIterator);
    if (log.isDebugEnabled()) {
        for (URL url : urls) {
            log.debug("Classpath entry: " + url);
        }
    }
    return new LaunchedURLClassLoader(urls, parent);
}

private URL[] getUrlsForArchives(Iterator<Archive> archivesIterator) {
    List<URL> urls = new ArrayList<>();
    while (archivesIterator.hasNext()) {
        Archive archive = archivesIterator.next();
        try {
            urls.add(archive.getUrl());
        }
        catch (MalformedURLException ex) {
            throw new IllegalStateException("Invalid URL", ex);
        }
    }
    return urls.toArray(new URL[0]);
}

getUrlsForArchives()方法用于获取每个JAR包或目录的URL,并将其添加到一个List中。最终,该方法返回一个表示所有URL的数组。然后,Spring Boot使用urls参数和parent参数创建LaunchedURLClassLoader

LaunchedURLClassLoader可以加载来自以下位置的类和资源:

  • 从应用程序的JAR包或目录中加载类和资源。
  • 从JRE和JDK提供的系统类或资源中加载类和资源。
  • 从其他位于Djava.ext.dirsjava.ext.dirs环境变量指定目录中的类或资源中加载类或资源。

LaunchedURLClassLoader确保只能从JAR包和目录中加载应用程序的类和资源,不会影响系统类或资源,这也是Spring Boot应用程序不会与其他应用程序或库发生冲突的原因之一。

使用LaunchedURLClassLoader加载应用程序的主类

在启动流程的下一步中,Spring Boot将使用LaunchedURLClassLoader加载应用程序的主类。应用程序的主类通常是一个类的入口点,其中包含Spring的@SpringBootApplication注解,该注解为Spring提供了一些配置信息。

Launcher类的main方法之前,Spring Boot会执行以下代码加载应用程序的主类:

private Class<?> getMainClass() throws Exception {
    String mainClassName = this.manifest.getMainAttributes().getValue(MAIN_CLASS_ATTRIBUTE);
    if (mainClassName == null) {
        throw new IllegalStateException("No '" + MAIN_CLASS_ATTRIBUTE + "' attribute specified in manifest");
    }
    try {
        return Class.forName(mainClassName, false, getClassLoader());
    }
    catch (ClassNotFoundException ex) {
        throw new IllegalStateException("Unable to load main class: " + mainClassName, ex);
    }
}

在这里,Spring Boot从应用程序的MANIFEST.MF文件中获取应用程序的主类名称,并使用获取的名称使用ClassLoader加载该类。一旦找到应用程序的主类,Spring Boot就会使用Java Reflection API执行该类的main方法。

加载Spring Boot默认配置

Spring Boot提供了大量的自动配置,这些自动配置确定了应用程序的基本行为。例如,自动配置可以启用Web功能和限制Web的端口,自动配置并启用Spring Data JPA或MongoDB,以及自动配置并启用Spring Security等。在启动流程中的下一步,Spring Boot会尝试读取一些默认的配置文件作为自动配置。这些默认的配置文件包括:

  • application.propertiesapplication.yml:位于应用程序的classpath根目录下,可以包含所有Spring Boot配置属性的值。
  • application-{profile}.propertiesapplication-{profile}.yml:根据特定的应用程序部署配置文件包括在激活的profile中。
  • application-local.propertiesapplication-local.yml:相当于application-{profile}.propertiesapplication-{profile}.yml,但只在部署到本地机器时才有效。

在确定了默认配置文件的位置之后,Spring Boot将使用PropertiesLoaderUtilsYamlPropertiesFactoryBean加载所有的配置文件,并将它们合并成一个PropertiesYamlProperties对象。

private ConfigurableEnvironment createEnvironment() {
    StandardEnvironment environment = new StandardEnvironment();
    if (this.useLegacyProcessing) {
        environment.getPropertySources().addLast(new SimpleCommandLinePropertySource(this.args));
    }
    else {
        environment.getPropertySources().addFirst(new SimpleCommandLinePropertySource(this.args));
    }
    MutablePropertySources sources = environment.getPropertySources();
    if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
        sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
    }
    ...
    new SpringFactoriesLoader().loadFactories(EnvironmentPostProcessor.class, classLoader)
            .forEach((processor) -> processor.postProcessEnvironment(environment, this.application));
    return environment;
}

在这里,createEnvironment()方法用于创建一个应用程序的环境,并通过将所有的配置文件加载到环境中来启用自动配置。在环境创建完成后,Spring Boot将检查所有被标记为@Configuration@Component的类,以确定它们是否是自动配置类。一旦自动配置类被确定,Spring Boot将尝试向应用程序中添加适当的Bean,以便为应用程序提供所需的功能。

执行Spring Boot应用程序

在前面的步骤中,Spring Boot创建了一个环境,并加载了必要的配置文件和自动配置类。在启动流程的最后一步中,Spring Boot将执行应用程序的主类,并开始运行应用程序。

private void launch(String[] args) throws Exception {
    ...
    evaluateSystemProperties();
    ...
    for (SpringApplicationRunListener listener : getRunListeners(args)) {
        listener.starting();
    }
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableApplicationContext context = createApplicationContext();
        ...
        context.publishEvent(new EnvironmentPreparedEvent(context, applicationArguments, this.environment));
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog());
        }
        ..
        context.refresh();
        ...
        propagateApplicationReadyEvent(listener, context);
        ...
        context.start

context.start()方法被调用后,Spring Boot将开始运行应用程序,这时应用程序已准备好处理来自客户端的请求。

结论

在本文中,我们对Spring Boot应用程序启动过程做了一个总体介绍,并深入了解了每个步骤的细节。了解Spring Boot启动流程是非常重要的,因为它可以使我们更好地理解应用程序的工作原理,并帮助我们在出现问题时更好地排除故障。通过对Spring Boot启动流程的深入学习,我们可以更好地维护和优化自己的应用程序。

在本文中,我们了解了以下内容:

  • Spring Boot创建LaunchedURLClassLoader,以加载应用程序的类和资源。
  • Spring Boot加载应用程序的主类,并使用Reflection API执行主方法。
  • Spring Boot加载配置文件,以准备自动配置。
  • Spring Boot执行应用程序,并开始运行。

了解Spring Boot启动流程是我们取得成功的关键之一。感谢您阅读本文。如果您有任何疑问或反馈,请在下面的评论区留言。