• SpringBoot启动流程梳理-自定义实现@SpringBootApplication注解


    1. Demo准备

    1. maven项目
    2. 实现@SpringBootApplication注解的模块
    3. 测试模块

    在这里插入图片描述

    2. SpringBootModule依赖

    <?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.2</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.jbp</groupId>
        <artifactId>SpringBootModule</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>SpringBootModule</name>
        <description>Demo project for Spring Boot</description>
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <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>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    3. SpringBootModule模块结构

    在这里插入图片描述

    4. 流程梳理

    4.1 定义启动注解

    在这里插入图片描述

    package com.jbp.springbootmodule;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Configuration
    @ComponentScan
    @Import(CustomImportSelect.class)
    public @interface CustomSpringBootApplication {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    4.2 自定义启动类的run方法的具体实现

    在这里插入图片描述

    run方法的大致逻辑:

    1. 创建一个Spring容器
    2. 创建Tomcat对象
    3. 生成DispatcherServlet对象,并且和前面创建出来的Spring容器进行绑定
    4. 将DispatcherServlet添加到Tomcat中
    5. 启动Tomcat

    代码:

    package com.jbp.springbootmodule;
    
    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.web.context.WebApplicationContext;
    import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
    import org.springframework.web.servlet.DispatcherServlet;
    
    import java.util.Collections;
    import java.util.Map;
    import java.util.stream.Collectors;
    
    /**
     * 描述:
     * 自定义的SpringBoot注解实现类
     *
     * @author XueGuCheng
     * @create 2022-08-11 0:13
     */
    public class CustomSpringApplication {
    
        public static void run(Class clazz) {
            // 1.创建一个Spring容器
            // 1.1 创建AnnotationConfigWebApplicationContext容器
            AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
            // 1.2 将启动类注册到Spring容器
            applicationContext.register(clazz);
            // 1.3 解析启动类上的注解(@ComponentScan解析到包扫描路径,如果为空,则将启动类所在的包路径做为扫描路径)从而就会扫描到UserService和UserController
            applicationContext.refresh();
    
            // 判断容器对象类型
            WebServer webServer = getWebServer(applicationContext);
            webServer.start();
    
            // 2.创建Tomcat对象
            // 3.生成DispatcherServlet对象,并且和前面创建出来的Spring容器进行绑定
            // 4.将DispatcherServlet添加到Tomcat中
            // 5.启动Tomcat
    //        startTomcat(applicationContext);
        }
    
        /**
         * 配置并启动Tomcat(向当前
         * Tomcat中添加了DispatcherServlet,并设置了一个Mapping关系,最后启动)
         * @param applicationContext 容器
         */
        public static void startTomcat(WebApplicationContext applicationContext) {
            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");
            StandardHost 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);
            tomcat.addServlet(contextPath, "dispatcher", new DispatcherServlet(applicationContext));
            context.addServletMappingDecoded("/*", "dispatcher");
            try {
                tomcat.start();
            } catch (LifecycleException e) {
                e.printStackTrace();
            }
        }
    
        public static WebServer getWebServer(WebApplicationContext applicationContext){
            // 获取Spring容器中的WebServer类型的bean对象
            Map<String, WebServer> webServer = applicationContext.getBeansOfType(WebServer.class);
            if (webServer.isEmpty() || webServer.size() > 1){
                throw new RuntimeException("WebServer类型的bean对象获取出错");
            }
            // 返回唯一bean
            return webServer.values().stream().findFirst().get();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    4.3 动态判断启动的容器类型(有Tomcat的依赖,那就启动Tomcat;有Jetty的依赖就启动Jetty)

    容器类型接口:

    package com.jbp.springbootmodule;
    
    /**
     * 描述:
     * 容器类型接口
     *
     * @author XueGuCheng
     * @create 2022-08-11 0:42
     */
    public interface WebServer {
    
        public void start();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Tomcat容器:

    package com.jbp.springbootmodule;
    
    /**
     * 描述:
     * tomcat容器
     *
     * @author XueGuCheng
     * @create 2022-08-11 0:44
     */
    public class TomcatWebServer implements WebServer{
    
        @Override
        public void start() {
            // 不做具体实现,简单打印用来验证
            System.out.println("===启动Tomcat===");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    Jetty容器 :

    package com.jbp.springbootmodule;
    
    /**
     * 描述:
     * Jetty容器
     *
     * @author XueGuCheng
     * @create 2022-08-11 0:46
     */
    public class JettyWebServer implements WebServer{
    
        @Override
        public void start() {
            System.out.println("===启动Jetty===");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Bean生效条件注解:

    package com.jbp.springbootmodule;
    
    import org.springframework.context.annotation.Conditional;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 描述:
     * 条件判断注解
     *
     * @author XueGuCheng
     * @create 2022-08-11 0:52
     */
    @Target({ElementType.TYPE,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Conditional(CustomConditionClass.class)
    public  @interface CustomConditionOnClass {
    
        String value() default "";
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    Bean生效条件的判断逻辑的具体实现:

    package com.jbp.springbootmodule;
    
    
    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    
    import java.util.Map;
    
    /**
     * 描述:
     * 容器类型判断逻辑的具体实现
     *
     * @author XueGuCheng
     * @create 2022-08-11 0:55
     */
    public class CustomConditionClass implements Condition {
    
        // 获取类上对应的注解属性,并通过类加载器进行匹配
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(CustomConditionOnClass.class.getName());
            String className = (String) annotationAttributes.get("value");
            try {
                context.getClassLoader().loadClass(className);
                return true;
            } catch (ClassNotFoundException e) {
                return false;
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    4.4 利用Java的spi机制使自动配置类生效

    在这里插入图片描述

    自动配置类接口:

    并在webServiceAutoConfiguration实现该接口

    package com.jbp.springbootmodule;
    
    public interface AutoConfiguration {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在@CustomSpringBootApplication注解中用@Import导入配置文件

    在这里插入图片描述

    CustomImportSelect:

    package com.jbp.springbootmodule;
    
    import org.springframework.context.annotation.DeferredImportSelector;
    import org.springframework.core.type.AnnotationMetadata;
    
    import java.util.ArrayList;
    import java.util.ServiceLoader;
    
    /**
     * 描述:
     * 导入配置文件
     *
     * @author XueGuCheng
     * @create 2022-08-11 1:23
     */
    public class CustomImportSelect implements DeferredImportSelector {
    
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            ServiceLoader<AutoConfiguration> serviceLoader = ServiceLoader.load(AutoConfiguration.class);
            ArrayList<Object> list = new ArrayList<>();
            for (AutoConfiguration autoConfiguration : serviceLoader) {
                list.add(autoConfiguration.getClass().getName());
            }
            return list.toArray(new String[0]);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
  • 相关阅读:
    线程池(重点)
    工地反光衣识别检测系统
    MySql 基础-(一)
    Python--控制台获取输入与正则表达式
    c单元语言测试--自定义代码、rspec 和Google test
    2.7HDR与LDR
    vim编辑器
    pwnable.kr--pwn游戏之fd
    antd4 Form表单验证的错误信息用Tooltip展示
    java-php-python-ssm-民航售票管理系统-计算机毕业设计
  • 原文地址:https://blog.csdn.net/xueguchen/article/details/126277390