文章目录

  • Spring Boot 源码
  • 一、Spring MVC
  • 1、项目启动流程
  • 2、请求访问流程
  • 3、目录结构
  • 4、代码
  • pom.xml 依赖
  • web.xml
  • Spring.xml
  • Spring Servlet
  • AppConfig 配置
  • 4、配置Tomcat
  • 二、Spring boot
  • 1、项目结构
  • 2、代码
  • pom.xml 依赖
  • @DilySpringBootApplication 自定义注解类
  • DefaultConfig 默认配置类
  • DilySpringApplication 静态加载类
  • TestApplication 启动类
  • 3、测试结果
  • 三、项目地址



Spring Boot 源码

从 spring MVC 的工作原理,从而来推断 spring boot 的启动流程,手动创建 springboot 项目

一、Spring MVC

1、项目启动流程

1、启动 Tomcat

2、解析 web.xml

web.xml 定义了要扫描的包

ContextLoaderListener.contextInitialized() ==> 创建Spring 父容器

3、实例化 DispatcherSerlet 对象

4、DispatcherSerlet 对象 init() ==> 创建 spring 容器

2、请求访问流程

springboot 使工程变为子模块_spring

3、目录结构

springboot 使工程变为子模块_xml_02

4、代码

pom.xml 依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ToolKit</artifactId>
        <groupId>com.dily</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>SpringMVC</artifactId>
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.10.RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

</project>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0">

    <!-- 定义 Tomcat 中的 Spring Servlet -->
    <servlet>
        <servlet-name>app</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!-- 配置文件路径 -->
            <param-value>/src/main/resources/WEB-INF/spring.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- 定义 servlet 和 url 的映射关系 -->
    <servlet-mapping>
        <servlet-name>app</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>
</web-app>
Spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

 	<!-- 定义扫描路径 -->
    <context:component-scan base-package="com.dily.springMVC"/>
</beans>
Spring Servlet
import com.dily.study.springMVC.config.AppConfig;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;

/**
 * Date: 2022-07-01 星期五
 * Time: 15:56
 * Author: Dily_Su
 * Remark: Spring MVC 创建 Servlet
 */
public class MyWebApplicationInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) {
        // 加载 Spring web config
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(AppConfig.class);

        // 设置父容器
        AnnotationConfigWebApplicationContext parent = new AnnotationConfigWebApplicationContext();
        parent.register(AppConfig.class);
        context.setParent(parent);

        // 新建一个 Servlet
        DispatcherServlet servlet = new DispatcherServlet(context);
        // 设置 Servlet 名称
        ServletRegistration.Dynamic registration = servletContext.addServlet("app",servlet);
        // 是否在启动时加载
        registration.setLoadOnStartup(1);
        // Servlet 对应的路径
        registration.addMapping("/app/*");
    }
}
AppConfig 配置
import org.springframework.context.annotation.ComponentScan;

/**
 * Date: 2022-07-01 星期五
 * Time: 15:58
 * Author: Dily_Su
 * Remark: Spring MVC Servlet 容器配置扫描包
 */

@ComponentScan("com.dily.study.demo") // 扫描包
public class AppConfig {
}

4、配置Tomcat

idea 中配置 Tomcat,启动

二、Spring boot

手动搭建 Spring Boot 项目,由此深入 Spring Boot 源码

1、项目结构

springboot 使工程变为子模块_mvc_03

2、代码

pom.xml 依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.dily.study</groupId>
    <artifactId>springboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot</name>
    <description>springboot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <!--            不使用 tomcat 时-->
            <!--            <exclusions>-->
            <!--                <exclusion>-->
            <!--                    <groupId>org.springframework.boot</groupId>-->
            <!--                    <artifactId>spring-boot-starter-tomcat</artifactId>-->
            <!--                </exclusion>-->
            <!--            </exclusions>-->
        </dependency>
        <!--            使用 jetty 时-->
        <!--        <dependency>-->
        <!--            <groupId>org.springframework.boot</groupId>-->
        <!--            <artifactId>spring-boot-starter-jetty</artifactId>-->
        <!--        </dependency>-->
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
@DilySpringBootApplication 自定义注解类
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import java.lang.annotation.*;

/**
 * Date: 2022-07-04 星期一
 * Time: 10:42
 * Author: Dily_Su
 * Remark: 自定义注解
 */
@Target(ElementType.TYPE)                 // 作用范围 类、接口、枚举
@Retention(RetentionPolicy.RUNTIME)       // 作用时间 运行时
@Documented                               // 该注解标记为公共 API, 可被文档化
@Inherited                                // 该注解是可被使用该注解的类继承
@Configuration                            // 声明为配置类
@ComponentScan                            // 扫描被注解对象所在目录
public @interface DilySpringBootApplication {
}
DefaultConfig 默认配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;

/**
 * Date: 2022-07-04 星期一
 * Time: 11:09
 * Author: Dily_Su
 * Remark: 默认配置类
 */
@Configuration
public class DefaultConfig {

    /**
     * 配置默认的 Spring servlet 容器
     * @return DispatcherServlet
     */
    @Bean
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }
}
DilySpringApplication 静态加载类
import org.apache.catalina.*;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.startup.Tomcat;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

/**
 * Date: 2022-07-04 星期一
 * Time: 10:47
 * Author: Dily_Su
 * Remark: 静态加载类
 */
public class DilySpringApplication {

    public static ConfigurableApplicationContext run(Class config) {
        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        applicationContext.register(config);
        applicationContext.refresh();

        // spring boot 源码中 根据 pom.xml 中依赖 选择使用 tomcat / jetty / undertow, 默认使用 tomcat
        // 这里直接默认 tomcat
        startTomcat(applicationContext);

        return applicationContext;
    }

    /**
     * 创建 Tomcat 并启动
     * @param applicationContext spring 容器
     * @return Tomcat
     */
    private static Tomcat startTomcat(AnnotationConfigWebApplicationContext applicationContext) {
        // 创建和配置 Tomcat
        Tomcat tomcat = new Tomcat();

        Server server = tomcat.getServer();
        Service service = server.findService("Tomcat");

        Connector connector = new Connector();
        connector.setPort(8081);

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

        // 从 IOC 容器中 获取 Spring Servlet
        DispatcherServlet defaultDispatcherServlet = applicationContext.getBean(DispatcherServlet.class);
        
        // 将获取到的 Spring servlet 加入到 tomcat 中
        tomcat.addServlet(contextPath, "dispatcher", defaultDispatcherServlet);
        context.addServletMappingDecoded("/*","dispatcher");

        try {
            // 启动 tomcat
            tomcat.start();
        }catch (LifecycleException e){
            e.printStackTrace();
        }

        return tomcat;
    }
}
TestApplication 启动类
import com.dily.study.springboot.annotation.DilySpringBootApplication;

/**
 * Date: 2022-07-04 星期一
 * Time: 10:48
 * Author: Dily_Su
 * Remark: 启动类
 */
// 使用自定义注解
@DilySpringBootApplication
public class TestApplication {

    public static void main(String[] args) {
        // 调用静态资源加载类的 run()
        DilySpringApplication.run(TestApplication.class);
    }
}

TestController 测试接口

package com.dily.study.springboot.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Date: 2022-07-04 星期一
 * Time: 10:40
 * Author: Dily_Su
 * Remark: 测试接口
 */

@RestController
public class TestController {

    @RequestMapping("/test")
    public String test() {
        return "springboot";
    }
}

3、测试结果

springboot 使工程变为子模块_mvc_04

三、项目地址

代码地址: Spring MVC 代码地址: Spring Boot