自己手写的SpringBoot启动器, 是一个学习了解SpringBoot启动逻辑和了解springboot原理的不错的实践Demo. 废话不多说,直接上代码:
项目结构
maven多项目结构,
myspringboot 自己手写的SpringBoot启动器
service-demo 用来测试SpringBoot启动器的示例项目

项目pom依赖
1. 主项目Maven Pom
<?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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>cn.tekin</groupId>
        <artifactId>myspringboot-app</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>myspringboot</artifactId>
    <packaging>jar</packaging>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-version>5.3.29</spring-version>
        <tomcat-version>9.0.80</tomcat-version>
    </properties>
    <dependencies>
        <!-- https://mavenlibs.com/maven/dependency/org.springframework/spring-web -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <!-- https://mavenlibs.com/maven/dependency/org.apache.tomcat.embed/tomcat-embed-core -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>${tomcat-version}</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
        <!--  这个插件用于生成可执行jar文件或者war文档 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>2. 测试项目Maven Pom
<?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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>cn.tekin</groupId>
        <artifactId>myspringboot-app</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>service-demo</artifactId>
    <packaging>jar</packaging>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>cn.tekin</groupId>
            <artifactId>myspringboot</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- https://mavenlibs.com/maven/dependency/org.redisson/redisson -->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.23.3</version>
        </dependency>
        <dependency>
            <groupId>de.ruedigermoeller</groupId>
            <artifactId>fst</artifactId>
            <version>2.57</version>
        </dependency>
    </dependencies>
</project>手写SpringBoot启动器项目核心代码
自定义的SpringBootApplication启动类
package cn.tekin;
import cn.tekin.webserver.MyWebServer;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import java.util.Map;
/**
 * 自定义的SpringBootApplication启动类
 */
public class MySpringbootApplication {
    public static void run(Class clazz, String[] args) {
        // 创建一个Spring容器
        AnnotationConfigWebApplicationContext applicationContext= new AnnotationConfigWebApplicationContext();
        // 注册启动类
        applicationContext.register(clazz);
        // 刷新容器
        applicationContext.refresh();
      // 从spring容器中获取WebServer bean 这样就可以解耦 避免if else判断要使用那个WebServer了
        MyWebServer webserver = getWebServer(applicationContext);
        webserver.start(applicationContext);
    }
    /**
     * 这里通过使用上下文中的 getBeansOfType 方法,通过将接口类来获取容器中的所有实现类,从而达到解耦的目的.
     * @param applicationContext
     * @return
     */
    private static MyWebServer getWebServer(WebApplicationContext applicationContext) {
        // 从spring容器中获取WebServer Bean对象
        Map<String, MyWebServer> webservers = applicationContext.getBeansOfType(MyWebServer.class);
        if (webservers.isEmpty()) {
            throw new NullPointerException();
        }
        // 返回map中的第一个对象; 如果有多个Bean, 这里只返回第一个
        return webservers.values().stream().findFirst().get();
    }
}
自定义的SpringBoot注解 @MySpringbootApp
package cn.tekin.anno;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 自定义的Spingboot注解
 *
 * 注意:这里@ComponentScan的没有指定扫描的基础包, spring默认会扫描run参数中传递的类所在的包下面的所有类
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Configuration
@ComponentScan(basePackages = {"cn.tekin.config","ws.yunnan.demo"})
public @interface MySpringbootApp {
}
自定义的webserver服务自动配置类 MyWebServerAutoConfiguration
package cn.tekin.config;
import cn.tekin.condition.MyJettyCondition;
import cn.tekin.webserver.MyJettyWebServer;
import cn.tekin.condition.MyTomcatCondition;
import cn.tekin.webserver.MyTomcatWebServer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyWebServerAutoConfiguration implements MyAutoConfiguration {
    @Bean
    @Conditional(MyTomcatCondition.class)
    public MyTomcatWebServer tomcatWebServer() {
        return new MyTomcatWebServer();
    }
    @Bean
    @Conditional(MyJettyCondition.class)
    public MyJettyWebServer jettyWebServer() {
        return new MyJettyWebServer();
    }
}
自定义的Tomcat服务启动类 MyTomcatWebServer
package cn.tekin.webserver;
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.servlet.DispatcherServlet;
public class MyTomcatWebServer implements MyWebServer {
    @Override
    public void start(WebApplicationContext applicationContext) {
        Tomcat tomcat = new Tomcat();
        Server server = tomcat.getServer();
        Service service = server.findService("Tomcat");
        Connector connector = new Connector();
        connector.setPort(8089);
        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);
        tomcat.addServlet(contextPath, "dispatcher", new DispatcherServlet(applicationContext));
        context.addServletMappingDecoded("/*", "dispatcher");
        try {
            tomcat.start();
        } catch (LifecycleException e) {
            e.printStackTrace();
        }
        System.out.println("Tomcat Started");
    }
}
@Conditional条件注解 条件判断类 MyTomcatCondition
package cn.tekin.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class MyTomcatCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        try {
            // 通过从上下文中 加载Tomcat的核心类来判断pom中是否添加了Tomcat依赖
            conditionContext.getClassLoader().loadClass("org.apache.catalina.startup.Tomcat");
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}
service-demo MySpringBootApp服务测试代码
package ws.yunnan.demo;
import cn.tekin.MySpringbootApplication;
import cn.tekin.anno.MySpringbootApp;
@MySpringbootApp
public class MyApplication {
    public static void main(String[] args) {
        // 这里的MyApplication.class 就是传入Spring容器的配置类, 一般是使用的当前的类, 也可以是其他的类
        MySpringbootApplication.run(MyApplication.class, args);
    }
}完整项目代码见
Gitee码云
 https://gitee.com/tekintian/myspringboot-app
Github
 https://github.com/tekintian/myspringboot-app










