手写模拟SpringBoot核心流程(二):实现Tomcat和Jetty的切换

这篇具有很好参考价值的文章主要介绍了手写模拟SpringBoot核心流程(二):实现Tomcat和Jetty的切换。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

实现Tomcat和Jetty的切换

前言

上一篇文章我们聊到,SpringBoot中内置了web服务器,包括Tomcat、Jetty,并且实现了SpringBoot启动Tomcat的流程。

那么SpringBoot怎样自动切换成Jetty服务器呢?

接下来我们继续学习如何实现Tomcat和Jetty的自动切换。

定义WebServer接口并实现

package com.ber.springboot;  
  
import org.springframework.web.context.WebApplicationContext;  
  
/**  
 * @Author 鳄鱼儿  
 * @Description TODO  
 * @date 2023/8/19 19:44  
 * @Version 1.0  
 */public interface WebServer {  
    void start(WebApplicationContext applicationContext);  
}

将BerSpringApplication类中startTomcat写到TomcatWebServer实现类中。

package com.ber.springboot;  
  
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;  
  
/**  
 * @Author 鳄鱼儿  
 * @Description TODO  
 * @date 2023/8/19 19:45  
 * @Version 1.0  
 */  
public class TomcatWebServer implements WebServer{  
    @Override  
    public void start(WebApplicationContext applicationContext) {  
        System.out.println("启动Tomcat");  
        Tomcat tomcat = new Tomcat();  
  
        Server server = tomcat.getServer();  
        Service service = server.findService("Tomcat");  
  
        Connector connector = new Connector();  
        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);  
  
        tomcat.addServlet(contextPath, "dispatcher", new DispatcherServlet(applicationContext));  
        context.addServletMappingDecoded("/*", "dispatcher");  
  
        try {  
            tomcat.start();  
        } catch (LifecycleException e) {  
            e.printStackTrace();  
        }  
    }  
}

JettyWebServer类同样实现WebServer接口,不过具体启动Jetty代码省略,不在本文探讨范围内。

package com.ber.springboot;  
  
/**  
 * @Author 鳄鱼儿  
 * @Description TODO  
 * @date 2023/8/19 19:46  
 * @Version 1.0  
 */  
public class JettyWebServer implements WebServer{  
    @Override  
    public void start() {  
        System.out.println("启动Jetty");  
    }  
}

修改BerSpringApplication类

package com.ber.springboot;  
  
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;  
  
import java.util.Map;  
  
/**  
 * @Author 鳄鱼儿  
 * @Description TODO  
 * @date 2023/8/19 14:08  
 * @Version 1.0  
 */  
public class BerSpringApplication {  
    public static void run(Class clazz) {  
        // 1. 创建Spring 容器  
        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();  
        applicationContext.register(clazz);  
        applicationContext.refresh();  
  
        // 2. 获取特定WebServer类型的Bean  
        WebServer webServer = getWebServer(applicationContext);  
        // 3. 调用start方法  
        webServer.start(applicationContext);  
  
    }  
  
    private static WebServer getWebServer(AnnotationConfigWebApplicationContext applicationContext) {  
        // key为beanName, value为Bean对象  
        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();  
    }  
}

在run方法中,获取到特定的web服务器,并通过start方法进行 启动。

getWebServer方法实现判断web服务器,并处理特殊情况——没有web服务器或者出现多个web服务器。

条件注解

package com.ber.springboot;  
  
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 鳄鱼儿  
 * @Description TODO  
 * @date 2023/8/19 20:06  
 * @Version 1.0  
 */  
@Target({ElementType.TYPE, ElementType.METHOD})  
@Retention(RetentionPolicy.RUNTIME)  
@Conditional(BerOnClassConsition.class)  
public @interface BerConditionalOnClass {  
    String value() default "";  
}

具体步骤为:

  1. 拿到@BerConditionalOnClass中的value属性
  2. 类加载器进行加载,加载到了特定的类名,则符合条件;否则不符合条件
package com.ber.springboot;  
  
import org.springframework.context.annotation.Condition;  
import org.springframework.context.annotation.ConditionContext;  
import org.springframework.core.type.AnnotatedTypeMetadata;  
  
import java.util.Map;  
  
/**  
 * @Author 鳄鱼儿  
 * @Description TODO  
 * @date 2023/8/19 20:08  
 * @Version 1.0  
 */  
public class BerOnClassConsition implements Condition {  
    @Override  
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {  
        Map<String, Object> annotationAttributes =  
                metadata.getAnnotationAttributes(BerConditionalOnClass.class.getName());  
        // 1. 拿到@BerConditionalOnClass中的value属性  
        String className = (String) annotationAttributes.get("value");  
  
        // 2. 类加载器进行加载  
        try {  
            // 2.1 加载到了特定的类名,则符合条件 true            context.getClassLoader().loadClass(className);  
            return true;  
        } catch (ClassNotFoundException e) {  
            // 2.2 加载不到,则不符合条件 false            return false;  
        }  
    }  
}

自动配置类

package com.ber.springboot;  
  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
  
/**  
 * @Author 鳄鱼儿  
 * @Description TODO  
 * @date 2023/8/19 20:34  
 * @Version 1.0  
 */  
@Configuration  
public class WebServiceAutoConfiguration implements AutoConfiguration{  
  
    @Bean  
    @BerConditionalOnClass("org.apache.catalina.startup.Tomcat")  
    public TomcatWebServer tomcatWebServer() {  
        return new TomcatWebServer();  
    }  
  
    @Bean  
    @BerConditionalOnClass("org.eclipse.jetty.server.Server")  
    public JettyWebServer jettyWebServer() {  
        return new JettyWebServer();  
    }  
}

自动配置类在Spring Boot应用程序中起着关键的作用,它们是实现自动化配置的核心组件。

这里定义满足各自条件的Bean,当org.apache.catalina.startup.Tomcat类存在时,TomcatWebServer的Bean才存在,另一个亦是如此。

当spring容器存在Bean时,就可以通过BerSpringApplication类getWebServer方法中的applicationContext.getBeansOfType(WebServer.class)获取到,并由此可以进行对web服务器是否存在的判断。

SPI机制发现WebServiceAutoConfiguration

刚刚我们定义了自动配置类,但运行user模块的Userapplication启动类时,发现是无法发现WebServiceAutoConfiguration配置类的。

这是因为我们传入了Userapplication作为配置类,扫描路径为Userapplication所在的包路径,是无法扫描到WebServiceAutoConfiguration类的。

在springboot中实现了类似SPI的思想,就是项目中的spring.factories文件,提供了一种可插拔的扩展机制,使开发人员能够轻松地定制应用程序的行为和功能,同时又能保持主应用程序的稳定性。

这里我们可以借助JDK的SPI机制实现发现WebServiceAutoConfiguration类。

在springboot模块中增加resources/META-INF/services/com.ber.springboot.AutoConfiguration文件,具体路径如图所示:

手写模拟SpringBoot核心流程(二):实现Tomcat和Jetty的切换,微服务专题,spring boot,tomcat,jetty

com.ber.springboot.WebServiceAutoConfiguration

增加AutoConfiguration接口类和实现类。

package com.ber.springboot;  
  
/**  
 * @Author 鳄鱼儿  
 * @Description TODO  
 * @date 2023/8/19 21:08  
 * @Version 1.0  
 */  
public interface AutoConfiguration {  
}
package com.ber.springboot;  
  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
  
/**  
 * @Author 鳄鱼儿  
 * @Description TODO  
 * @date 2023/8/19 20:34  
 * @Version 1.0  
 */  
@Configuration  
public class WebServiceAutoConfiguration implements AutoConfiguration{  
  
    @Bean  
    @BerConditionalOnClass("org.apache.catalina.startup.Tomcat")  
    public TomcatWebServer tomcatWebServer() {  
        return new TomcatWebServer();  
    }  
  
    @Bean  
    @BerConditionalOnClass("org.eclipse.jetty.server.Server")  
    public JettyWebServer jettyWebServer() {  
        return new JettyWebServer();  
    }  
}

并在注解类@BerSpringBootApplication上增加@Import(BerImportSelect.class)注解,BerImportSelect类从com.ber.springboot.AutoConfiguration文件中获取类名,然后添加到spring容器。

package com.ber.springboot;  
  
import org.springframework.context.annotation.DeferredImportSelector;  
import org.springframework.core.type.AnnotationMetadata;  
  
import java.util.ArrayList;  
import java.util.List;  
import java.util.ServiceLoader;  
  
/**  
 * @Author 鳄鱼儿  
 * @Description 
 * @date 2023/8/19 21:15  
 * @Version 1.0  
 */  
public class BerImportSelect implements DeferredImportSelector {  
    @Override  
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {  
        /** 使用Java的ServiceLoader机制加载实现了AutoConfiguration接口的类  
         * AutoConfiguration是Spring Boot中用于自动配置的接口  
         * AutoConfiguration的实现类通常包含了一些配置信息,帮助应用程序在不需要显式配置的情况下自动完成一些功能  
         */  
        ServiceLoader<AutoConfiguration> serviceLoader = ServiceLoader.load(AutoConfiguration.class);  
  
        List<String> list = new ArrayList<>();  
        for (AutoConfiguration autoConfiguration : serviceLoader) {  
            list.add(autoConfiguration.getClass().getName());  
        }  
  
        // 返回包含所有加载的AutoConfiguration实现类名的字符串数组  
        return list.toArray(new String[0]);  
    }  
}

添加Jetty依赖

修改user模块的依赖如下:

<?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>simulate-springboot</artifactId>  
        <groupId>org.example</groupId>  
        <version>1.0-SNAPSHOT</version>  
    </parent>  
    <modelVersion>4.0.0</modelVersion>  
  
    <artifactId>user</artifactId>  
  
    <properties>  
        <maven.compiler.source>8</maven.compiler.source>  
        <maven.compiler.target>8</maven.compiler.target>  
    </properties>  
  
    <dependencies>  
        <dependency>  
            <groupId>org.example</groupId>  
            <artifactId>springboot</artifactId>  
            <version>1.0-SNAPSHOT</version>  
            <exclusions>  
                <exclusion>  
                    <groupId>org.apache.tomcat.embed</groupId>  
                    <artifactId>tomcat-embed-core</artifactId>  
                </exclusion>  
            </exclusions>  
        </dependency>  
  
        <dependency>  
            <groupId>org.eclipse.jetty</groupId>  
            <artifactId>jetty-server</artifactId>  
            <version>9.4.43.v20210629</version>  
        </dependency>  
    </dependencies>  
  
</project>

这里需要排除tomcat依赖,因为springboot中已经添加了tomcat的依赖。

不排除就会出来既有tomcat又有Jetty,就会出现IllegalStateException异常。

到此运行user模块的UserApplication类就可以啦。文章来源地址https://www.toymoban.com/news/detail-661742.html

到了这里,关于手写模拟SpringBoot核心流程(二):实现Tomcat和Jetty的切换的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SpringBoot---内置Tomcat 配置和切换

    😀前言 本篇博文是关于内置Tomcat 配置和切换,希望你能够喜欢 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀,希望我的文章可以帮助到大家,您的满意是我的动力😉😉 💕欢迎大家:这里是CSDN,我总结知识的地方,欢迎来到我的博客,感谢大家的观看🥰 如果

    2024年02月12日
    浏览(26)
  • 从零手写实现 apache Tomcat-01-入门介绍

    要实现一个简单版本的Tomcat,整体思路如下 了解 Tomcat 的基本原理 : Tomcat 是一个开源的 Java Servlet 容器和 Web 服务器,它能够运行 Java Servlet 和 JavaServer Pages。 Tomcat 是基于 Java 的,它是用 Java 编写的。 创建一个简单的 HTTP 服务器 : 创建一个 Java 类,作为你的 HTTP 服务器的入

    2024年04月15日
    浏览(36)
  • SpringBoot内嵌Tomcat启动流程

    Spring MVC 让开发者不用了解 Servlet 细节,专注于 Controller 编写 API 接口。Spring Boot 更是采用约定大于配置的设计思想,通过内嵌 Tomcat 的方式让开发者可以快速构建并部署一个 Web 应用。怎么做到的呢? 早期的开发,一般是基于 Spring 和 Spring MVC 构建我们的应用,然后把项目打

    2024年02月02日
    浏览(31)
  • SpringBoot源码学习4——SpringBoot内嵌Tomcat启动流程源码分析

    系列文章目录和关于我 我在初学spring的时候,很懵逼,因为整个项目中不存在main方法,让我有点摸不着头脑。那时候我知道有个东西叫tomcat是它监听了端口,解析了协议调到了我的servlet。 在我初学SpringBoot的时候,很懵逼,有main方法了,但是tomcat在哪里呢,又是如何启动起

    2024年02月04日
    浏览(32)
  • SpringBoot核心原理以及工作流程

    1. SpringBoot 概述 SpringBoot 是Spring的一套快速配置脚手架,快速的将一些常用的第三方依赖整合(原理:通过Maven子父工程的方式),简化xml配置,全部采用注解形式,内嵌web应用容器(如:jetty和Tomcat),最终以java应用程序进行执行 2. SpringBoot的启动类入口 2.1 @SpringBootApplicat

    2024年02月01日
    浏览(40)
  • SpringBoot配置外部Tomcat项目启动流程源码分析

    SpringBoot应用默认以Jar包方式并且使用内置Servlet容器(默认Tomcat),该种方式虽然简单但是默认不支持JSP并且优化容器比较复杂。故而我们可以使用习惯的外置Tomcat方式并将项目打War包。 ① 同样使用Spring Initializer方式创建项目 ② 打包方式选择\\\"war\\\" ③ 选择添加的模块 ④ 创建的

    2024年02月04日
    浏览(30)
  • SpringBoot3自动配置流程 SPI机制 核心注解 自定义starter

    导入 starter 依赖导入 autoconfigure 寻找类路径下 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件 启动,加载所有 自动配置类 xxxAutoConfiguration 给容器中配置功能 组件 组件参数 绑定到 属性类 中。 xxxProperties 属性类 和 配置文件 前缀项绑定 @Contional 派生的条件

    2024年02月16日
    浏览(34)
  • Springboot tomcat bean 默认作用域 singleton 情况下模拟线程不安全情况 设置多例方式 prototype

    目录 写一个控制层的类 验证方法 ​编辑 分别执行如下请求,先执行等待时间久的 日志结果 结论 配置多例模式 配置文件application.properties 类加注解 配置类方式 增加验证 控制层  服务层 都是  singleton 模式情况 模拟线程不安全情况 service 代码 ctr 测试方式 运行日志 结论

    2024年02月12日
    浏览(28)
  • 34、springboot切换内嵌Web服务器(Tomcat服务器)与 生成SSL证书来把项目访路径从 HTTP 配置成 HTTPS

    知识点1:springboot切换内嵌Web服务器(Tomcat服务器) 知识点2:生成SSL证书来把项目访路径从 HTTP 配置成 HTTPS spring-boot-starter-web 默认依赖 Tomcat 内置服务器 改为 Jetty 服务器 改为 Undertow 服务器 目的:把请求路径 http://xxxxx 改成 https://xxxxx 如图:原本普通的项目,启动后是http的

    2024年02月11日
    浏览(42)
  • 根据源码,模拟实现 RabbitMQ - 从需求分析到实现核心类(1)

    目录 一、需求分析 1.1、对 Message Queue 的认识 1.2、消息队列核心概念 1.3、Broker Server 内部关键概念 1.4、Broker Server 核心 API (重点实现) 1.5、交换机类型 Direct 直接交换机 Fanout 扇出交换机 Topic 主题交换机 1.6、持久化 1.7、网络通信 通信流程 远程调用设计思想 1.8、模块设计图

    2024年02月12日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包