我们一起聊聊如何手写一个SpringBoot框架

开发 前端
在这篇文章中,我们将手写模拟SpringBoot的核心流程,让大家能够以一种简单的方式了解SpringBoot的大概工作原理。

你好,我是柳岸花开。

在这篇文章中,我们将手写模拟SpringBoot的核心流程,让大家能够以一种简单的方式了解SpringBoot的大概工作原理。

项目结构

我们创建一个工程,包含两个模块:

  1. springboot模块,表示SpringBoot框架的源码实现。
  2. user包,表示用户业务系统,用来写业务代码来测试我们所模拟出来的SpringBoot。

首先,在springboot模块中添加以下依赖:

<dependencies>
    <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>

在user模块下,我们进行正常的开发,比如先添加SpringBoot依赖,然后定义相关的Controller和Service:

<dependencies>
    <dependency>
        <groupId>org.example</groupId>
        <artifactId>springboot</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

定义UserController和UserService

在user包下定义UserController和UserService,希望能通过运行MyApplication中的main方法,直接启动项目,并能在浏览器中正常访问到UserController中的某个方法。

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("test")
    public String test(){
        return userService.test();
    }
}

核心注解和核心类:

  • @SpringBootApplication:这个注解加在应用启动类上,即main方法所在的类。
  • SpringApplication:这个类中有个run()方法,用来启动SpringBoot应用。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Configuration
@ComponentScan
public @interface BobSpringBootApplication {
}

public class BobSpringApplication {

    public static void run(Class clazz){
        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        applicationContext.register(clazz);
        applicationContext.refresh();

        startTomcat(applicationContext);
    }

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

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(context, "dispatcher", new DispatcherServlet(applicationContext));
        context.addServletMappingDecoded("/", "dispatcher");

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

        tomcat.getServer().await();
    }
}

@BobSpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        BobSpringApplication.run(MyApplication.class);
    }
}

实现Tomcat和Jetty的切换

如果项目中有Tomcat的依赖,那就启动Tomcat;如果有Jetty的依赖就启动Jetty;如果两者都没有则报错;如果两者都有也报错。

public interface WebServer {
    void start();
}

public class TomcatWebServer implements WebServer {
    @Override
    public void start() {
        System.out.println("启动Tomcat");
    }
}

public class JettyWebServer implements WebServer {
    @Override
    public void start() {
        System.out.println("启动Jetty");
    }
}

模拟实现条件注解:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(BobOnClassCondition.class)
public @interface BobConditionalOnClass {
    String value() default "";
}

public class BobOnClassCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(BobConditionalOnClass.class.getName());
        String className = (String) annotationAttributes.get("value");

        try {
            context.getClassLoader().loadClass(className);
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}

模拟实现自动配置类:

@Configuration
public class WebServiceAutoConfiguration {
    @Bean
    @BobConditionalOnClass("org.apache.catalina.startup.Tomcat")
    public TomcatWebServer tomcatWebServer() {
        return new TomcatWebServer();
    }

    @Bean
    @BobConditionalOnClass("org.eclipse.jetty.server.Server")
    public JettyWebServer jettyWebServer() {
        return new JettyWebServer();
    }
}

在BobSpringApplication中添加获取WebServer的方法:

public static WebServer getWebServer(ApplicationContext applicationContext) {
    Map<String, WebServer> webServers = applicationContext.getBeansOfType(WebServer.class);

    if (webServers.isEmpty()) {
        throw new NullPointerException();
    }
    if (webServers.size() > 1) {
        throw new IllegalStateException();
    }

    return webServers.values().stream().findFirst().get();
}

通过以上步骤,我们实现了一个简单的SpringBoot,并且可以根据依赖切换Tomcat和Jetty。

责任编辑:武晓燕 来源: 柳岸花开
相关推荐

2024-06-17 11:59:39

2022-06-26 09:40:55

Django框架服务

2024-07-03 08:36:14

序列化算法设计模式

2023-08-04 08:20:56

DockerfileDocker工具

2023-09-10 21:42:31

2023-08-10 08:28:46

网络编程通信

2023-06-30 08:18:51

敏捷开发模式

2022-05-24 08:21:16

数据安全API

2021-08-27 07:06:10

IOJava抽象

2024-02-20 21:34:16

循环GolangGo

2024-11-27 16:07:45

2024-09-30 09:33:31

2023-04-03 00:09:13

2024-09-09 00:00:00

编写技术文档

2024-09-04 08:55:56

2021-10-14 11:08:17

大数据框架内存

2021-12-10 07:45:48

字节音频视频

2024-12-10 00:00:25

2024-08-05 10:55:52

2021-11-04 06:58:31

CSS性能设备
点赞
收藏

51CTO技术栈公众号