前言

相信每一个 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() 方法。