手写 Spring Boot 的启动流程

前言

相信每一个 Java 开发者对于 Spring Boot 都不会陌生, Spring Boot 的出现,极大的简化了我们开发 web 应用的难度。此外,Spring Boot 还具有如下优势:

  1. 简化配置:Spring Boot 提供了默认配置,减少了开发人员需要进行手动配置的工作,从而提高了开发效率。

  2. 快速开发:Spring Boot 提供了许多开箱即用的功能和模板,使开发人员能够快速构建应用程序,减少了样板代码的编写。

  3. 内嵌式服务器:Spring Boot 支持内嵌式服务器(如 Tomcat、Jetty 等),无需额外配置,可以轻松部署应用程序。

  4. 自动化配置:Spring Boot 提供了自动配置机制,可以根据项目的依赖自动配置应用程序,减少了手动配置的繁琐工作。

一言以概之,Spring Boot 是基于 Spring 开发的一种轻量级的全新框架,不仅继承了 Spring 框架原有的优秀特性,而且还通过简化配置来进一步简化应用的搭建开发过程。

run 方法

在 Spring Boot 框架下,通常会按照如下所示的逻辑来编写启动类:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * SpringBoot应用启动类
 */
@SpringBootApplication
public class StartSpringBootApplication {

    public static void main(String[] args) {
        // 启动SpringBoot
        SpringApplication.run(StartSpringBootApplication.class, args);
    }
}
  • 提供一个 SpringBoot 的启动类 StartSpringBootApplication (注:启动类的名称可任意指定)

  • 在启动类上标注一个 @SpringBootApplication 注解

  • 编写一个 main 方法,调用 SpringApplication 中的 run 方法

接着,通过运行上述的 main 方法就可完成一个 Spring Boot 应用的启动。不难发现,启动一个 Spring Boot 应用非常简单。

正如之前分析的那样,Spring Boot 应用启动的核心秘密都在于 run 方法。

public ConfigurableApplicationContext run(String... args) {
    // .......省略其他无关代码
    listeners.starting();
    try {
        // 构建一个应用参数解析器
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        
        // 加载系统的属性配置信息
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        // 用于控制是否忽略BeanInfo的配置
        configureIgnoreBeanInfo(environment);
        // 打印banner信息
        Banner printedBanner = printBanner(environment);
        // 创建一个容器,类型为ConfigurableApplicationContext
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        // 容器准备工作 (可暂时忽略)
        prepareContext(context, environment, listeners, 
        // 解析传入参数信息
        applicationArguments, printedBanner);
        // 容器刷新 (重点关注)
        refreshContext(context);
        afterRefresh(context, applicationArguments);
       
    }
     // .......省略其他无关代码


    return context;
}

进一步,上述方法可总结为下图所示内容:

手写 Spring Boot 启动逻辑

引入依赖

  • Spring Boot 基于 Spring 架构,需要在 Spring Boot 模块中依赖 Spring

  • Spring Boot 也支持 Spring MVC 功能,依赖 Spring MVC 和 Tomcat 等

<dependency>  
  <groupId>org.springframework</groupId>  
  <artifactId>spring-context</artifactId> 
  <version>5.3.18</version>  
</dependency>  
<dependency>  
  <groupId>org.springframework</groupId>  
  <artifactId>spring-web</artifactId>  
  <version>5.3.18</version>  
</dependency>  
<dependency>  
  <groupId>org.springframework</groupId>  
  <artifactId>spring-webmvc</artifactId>  
  <version>5.3.18</version>  
</dependency>  
<dependency>  
  <groupId>javax.servlet</groupId>  
  <artifactId>javax.servlet-api</artifactId>  
  <version>4.0.1</version>  
</dependency>  
<dependency>  
  <groupId>org.apache.tomcat.embed</groupId>  
  <artifactId>tomcat-embed-core</artifactId>  
  <version>9.0.60</version>  
</dependency>  

实现 Spring Boot 简单功能

  • @SpringBootApplication 注解

    @Target(ElementType.TYPE)  
    @Retention(RetentionPolicy.RUNTIME)  
    @Configuration  
    @ComponentScan  
    public @interface BerSpringBootApplication {  
    }
  • SpringApplication 启动类

    public class SpringApplication {  
        public static void run(Class clazz) {  
            // 1. 创建Spring 容器  
            AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();  
            applicationContext.register(clazz);  
            applicationContext.refresh();  
      
            // 2. 创建并启动Tomcat  
            startTomcat(applicationContext);  
        }  
      
        private static void startTomcat(WebApplicationContext applicationContext) {  
            // 2.1 创建tomcat对象  
            Tomcat tomcat = new Tomcat();  
      
            Server server = tomcat.getServer();  
            Service service = server.findService("Tomcat");  
      
            Connector connector = new Connector();  
            // 设置默认tomcat启动端口  
            connector.setPort(8023);  
      
            Engine engine = new StandardEngine();  
            engine.setDefaultHost("localhost");  
      
            Host host = new StandardHost();  
            host.setName("localhost");  
      
            String contextPath = "";  
            Context context = new StandardContext();  
            context.setPath(contextPath);  
            context.addLifecycleListener(new Tomcat.FixContextListener());  
      
            host.addChild(context);  
            engine.addChild(host);  
      
            service.setContainer(engine);  
            service.addConnector(connector);  
      
            // 2.2 创建DispatcherServlet对象,并与Spring容器绑定,并将DispatcherServlet对象添加至Tomcat中  
            tomcat.addServlet(contextPath, "dispatcher", new DispatcherServlet(applicationContext));  
            context.addServletMappingDecoded("/*", "dispatcher");  
      
            // 2.3 启动tomcat  
            try {  
                tomcat.start();  
            } catch (LifecycleException e) {  
                e.printStackTrace();  
            }  
        }  
    }

在 Spring MVC 中,DispatcherServlet 就起到这个前端控制器的作用。DispatcherServlet 需要绑定一个 Spring 容器,当 DispatcherServlet 接收到请求后,就可以从绑定的 Spring 容器中找到所匹配的 Controller,并执行对应的方法。

因此,在 run 方法中实现了:

  1. 创建一个 Spring 容器

    1. 创建了一个 AnnotationConfigApplicationContext 容器,并通过传入的 clazz 作为容器的配置类。

  1. 创建并启动 Tomcat

    1. 创建 tomcat 对象

    2. 创建 DispatcherServlet 对象,并与 Spring 容器绑定,并将 DispatcherServlet 对象添加至 Tomcat 中

    3. 启动 Tomcat

那么,是如何将 clazz 作为配置类的呢?如下:

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

DemoApplication.class 传入 run 方法,DemoApplication 类就是 AnnotationConfigWebApplicationContext 容器的配置类。

配置过程:

创建并启动 Tomcat

startTomcat() 方法中,创建了 tomcat 对象,并对 tomcat 进行配置,如默认端口 8023,创建和配置 Tomcat 引擎和主机等。然后再创建 DispatcherServlet 对象,并与 Spring 容器绑定,并将 DispatcherServlet 对象添加至 Tomcat 中。

当运行 DemoApplication 启动类时,调用 SpringApplication 类中的 run 方法,所以在 run 方法中调用 startTomcat() 方法。


手写 Spring Boot 的启动流程
https://clear-wind.cn/archives/ksJzDN9c
作者
晴天
发布于
2025年04月17日
许可协议