手写模拟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 "";
}
具体步骤为:
- 拿到@BerConditionalOnClass中的value属性
- 类加载器进行加载,加载到了特定的类名,则符合条件;否则不符合条件
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文件,具体路径如图所示:
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类就可以啦。
相关文章:

手写模拟SpringBoot核心流程(二):实现Tomcat和Jetty的切换
实现Tomcat和Jetty的切换 前言 上一篇文章我们聊到,SpringBoot中内置了web服务器,包括Tomcat、Jetty,并且实现了SpringBoot启动Tomcat的流程。 那么SpringBoot怎样自动切换成Jetty服务器呢? 接下来我们继续学习如何实现Tomcat…...

Python土力学与基础工程计算.PDF-土的三项组成
5.3 Python求解 Python 求解代码如下: 1. # 定义已知参数 2. G_s 2.7 # 比重 3. w 0.2 # 含水量 4. e 0.6 # 孔隙比 5. gamma_w 9.81 # 水的重度 6. 7. # 根据公式计算饱和度 8. S_r G_s * w / e 9. print("饱和度为", S_r) 10. 11.…...
危化安全生产信息化平台在煤化领域的应用
一、背景介绍 煤化工行业是一个集煤炭、石油、化工等多种产业于一体的综合性行业,其特点是工艺流程复杂、设备繁多、安全隐患大。近年来,随着煤化工行业的快速发展,安全生产问题日益凸显。为了有效提高危化安全生产水平,某煤化工…...
Linux(CentOS)运维脚本工具集合
使用说明 备份指定目录 # 备份指定目录文件到指定目录,备份文件名称为:备份目录最后一层目录"_"日期.tar.gz # 第一个参数:backdir 第二参数:备份文件保存目录 第三个参数:备份目录/文件 sh script.sh backdir /root/…...

【Java alibabahutool】JSON、Map、实体对象间的相互转换
首先要知道三者的互转关系,可以先将JSON理解成是String类型。这篇博文主要是记录阿里巴巴的JSONObject的两个方法。toJSONString()以及parseObject()方法。顺便巩固Map与实体对象的转换技巧。 引入依赖 <!-- 阿里巴巴 JSON转换 以下二选一即可 没有去细研究两者…...

按软件开发阶段的角度划分:单元测试、集成测试、系统测试、验收测试
1.单元测试(Unit Testing) 单元测试,又称模块测试。对软件的组成单位进行测试,其目的是检验软件基本组成单位的正确性。测试的对象是软件里测试的最小单位:模块。 测试阶段:编码后或者编码前(…...

【python】Leetcode(primer-dict-list)
文章目录 260. 只出现一次的数字 III(字典 / 位运算)136. 只出现一次的数字(字典)137. 只出现一次的数字 II(字典)169. 求众数(字典)229. 求众数 II(字典)200…...

网络安全(黑客)入门
想自学网络安全(黑客技术)首先你得了解什么是网络安全!什么是黑客! 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术,而“蓝队”、“安全运营”、“安全…...

在CSS中,盒模型中的padding、border、margin是什么意思?
在CSS中,盒模型(Box Model)是用来描述和布局HTML元素的基本概念。它将每个HTML元素看作是一个矩形的盒子,这个盒子包括了内容(content)、内边距(padding)、边框(border&a…...

有线耳机插入电脑没声音
有线耳机插入电脑没声音 首先确保耳机和电脑都没问题,那就有可能是声音输出设备设置错误 右击任务栏的声音图标-打开声音设置-选择输出设备。...

【面试 反思】Retrofit源码与设计 7 连问
前言 在实际项目中往往是使用Retrofit来做网络请求工作。Retrofit采用RESTful风格,本质上只是对OkHttp进行封装,今天我们根据几个问题来进一步学习一下Retrofit的源码与设计思想。 1. 使用方法 直接看一下官方介绍的使用方法。 public final class S…...

flutter 雷达图
通过CustomPainter自定义雷达图 效果如下 主要代码 import package:flutter/material.dart; import dart:math; import dash_painter.dart; import model/charts_model.dart;class RadarChart extends StatelessWidget {final List<ChartModel> list;final double maxV…...
机器学习之损失函数(Loss Function)
损失函数(Loss Function)是机器学习和深度学习中的关键概念,它用于衡量模型的预测与实际目标之间的差异或误差。损失函数的选择对于模型的训练和性能评估至关重要,不同的任务和问题通常需要不同的损失函数。 以下是一些常见的损失…...

创邻科技张晨:图数据库,激活数据要素的新基建
“数据经济时代,数据要素产业链的各细分领域均蕴含机遇,图技术作为网络协同和数据智能的底层发动机,将深度掘金数字中国价值潜能”。 8月22日,在2023中国(南京)国际软件产品和信息服务交易博览会的信息技术…...

使用端口映射实现Spring Boot服务端接口的公网远程调试:详细配置与步骤解析
文章目录 前言1. 本地环境搭建1.1 环境参数1.2 搭建springboot服务项目 2. 内网穿透2.1 安装配置cpolar内网穿透2.1.1 windows系统2.1.2 linux系统 2.2 创建隧道映射本地端口2.3 测试公网地址 3. 固定公网地址3.1 保留一个二级子域名3.2 配置二级子域名3.2 测试使用固定公网地址…...
stm32之点亮LED
今天,记录一下stm32如何点亮一个LED,程序本身十分简单,但主要是学习编程的格式。 led.h #ifndef _led_H #define _led_H#include "system.h"/* LED时钟端口、引脚定义 */ #define LED1_PORT GPIOB #define LED1_PIN GPIO_Pin_5 #d…...
SA8000认证的难点及注意事项
SA8000认证是什么? SA8000即“社会责任标准”,是Social Accountability 8000的英文简称,由社会责任国际组织(SAI)制定与执行,是全球首个道德规范国际标准。自1997年问世以来,它创建了一个衡量社会责任的共同语言&#…...

Java可视化物联网智慧工地SaaS平台源码:人脸识别考勤
基于微服务JavaSpring Cloud Vue UniApp MySql实现的智慧工地云平台源码 智慧工地是指利用云计算、大数据、物联网、移动互联网、人工智能等技术手段,为建筑施工现场提供智能硬件及物联网平台的解决方案,以实现建筑工地的实时化、可视化、多元化、智慧化…...

告别数字化系统“物理叠加”,华为云推动智慧门店价值跃迁
文|智能相对论 作者|叶远风 有大屏幕滚动播放广告; 有人脸识别系统让消费者自助结账; 有订单管理系统综合分析一段时间内总体经营情况; 有全门店监控直连总部机房; …… 以搭载数字化系统的硬件设备为表面特征的智慧门店&a…...

k8s 常用命令(四)
12、删除pod中的nginx服务及service [rootmaster ~]# kubectl delete deployment nginx -n kube-public [rootmaster ~]# kubectl delete svc -n kube-public nginx-service 13、查看endpoint的信息 [rootmaster ~]# kubectl get endpoints 14、修改/更新(镜像、…...

超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...

论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...