手写模拟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、修改/更新(镜像、…...
大语言模型的分布式训练
什么是大语言模型 大语言模型(Large Language Model,缩写LLM),也称大型语言模型,是一种人工智能模型,旨在理解和生成人类语言。它们在大量的文本数据上进行训练,可以执行广泛的任务,包括文本总结、翻译、情感分析等等。LLM的特点是规模庞大,包含数十亿的参数,帮助它们学习语言…...
【JavaEE】Spring全家桶实现AOP-统一处理
【JavaEE】AOP(2) 文章目录 【JavaEE】AOP(2)1. 统一登录校验处理1.1 自定义拦截器1.2 将自定义拦截器加入到系统配置1.3 测试1.4 对于静态资源的处理1.5 小练习:统一登录拦截处理1.6 拦截器原理1.6.1 执行流程1.6.2 源…...
HQL解决连续三天登陆问题
1.背景 统计连续登录天数超过3天的用户,输出信息包括:用户id,登录天数,起始时间,结束时间; 2.准备数据 -- 建表 create table if not exists user_login_3days(user_id STRING,login_date date );--插入…...
(一)Docker简介(一篇足以)
一、简介 一个项目环境配置相当麻烦,如果换一台机器跑起来,所有配置就要重来一次,费力费时。很多人想到,能不能从根本上解决问题,软件可以带环境安装?也就是说,安装的时候,把原始环…...
RK3568 安卓源码编译
一.repo安卓编译工具 项目模块化/组件化之后各模块也作为独立的 Git 仓库从主项目里剥离了出去,各模块各自管理自己的版本。Android源码引用了很多开源项目,每一个子项目都是一个Git仓库,每个Git仓库都有很多分支版本,为了方便统…...
第4篇:vscode+platformio搭建esp32 arduino开发环境
第1篇:Arduino与ESP32开发板的安装方法 第2篇:ESP32 helloword第一个程序示范点亮板载LED 第3篇:vscode搭建esp32 arduino开发环境 1.配置默认安装路径,安装到D盘。 打开环境变量,点击新建 输入变量名PLATFORMIO_CORE_DIR与路径:D:\PLATF…...
2023前端面试笔记 —— CSS3
系列文章目录 内容链接2023前端面试笔记HTML52023前端面试笔记CSS3 文章目录 系列文章目录前言一、CSS选择器的优先级二、通过 CSS 的哪些方式可以实现隐藏页面上的元素三、px、em、rem之间有什么区别?四、让元素水平居中的方法有哪些五、在 CSS 中有哪些定位方式六…...
iOS 如何对整张图分别局部磨砂,并完全贴合
官方磨砂方式 - (UIVisualEffectView *)effectView{if(!_effectView){UIBlurEffect *blur [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];_effectView [[UIVisualEffectView alloc] initWithEffect:blur];}return _effectView; }使用这种方式对一张图的上半部分和…...
Packet_Tracer的使用
一、实验目的: 通过该实验了解Packet Tracer的使用方法,能够用Packet Tracer建立和模拟网络模型。 二、主要任务: 1.熟悉PT的界面,了解按键用途。 2.尝试自己建立一个小型网络,并测试连通性。 3.学习P…...
WPF如果未定义绑定的属性,程序如何处理
问题:wpf中,<Button IsEnabled"{Binding IsValid1}"></Button>,如果没定义绑定的属性IsValid1,可以正常用吗 解答:在 WPF 中,如果没有定义绑定的属性 IsValid1,会导致绑…...
做网站的软件wd的叫啥/百度网址链接是多少
前几天我在我的Blog上发布了NGuestBook(点击这里下载),得到了很多反馈,在这里非常感谢大家的关注和支持。一些朋友在E-mail中提到,这个NGuestBook和我那个系列文章《基于.NET平台的分层架构实战》中讲的Demo有非常多不…...
品牌网站建设策划书/seo描述快速排名
没有急停,需要花费0.5秒 有急停,需要花费0.25秒; 是否存在模拟急停的可能性? 结论是,存在 这种可能性。但是现在的代码框架行不通,进不到中断里边, 中断里,应该只干标志位就好&am…...
夺宝网站建设/如何做好口碑营销
简介:图像二值化就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的黑白效果的过程。 普通图像二值化 代码如下: import cv2 as cv import numpy as np #全局阈值 def threshold_demo(image): gray cv.cvtColor(image,…...
wordpress的登录界面/网络热词有哪些
文章目录前言1.awk命令1.1 BEGIN的作用1.2 END的作用1.3 FS内置变量的作用1.4 关系表达式作为条件2. 总结前言 awk标准输出就是printf命令。如下如这种情况下,用cut命令就不能解决,这是就需要awk命令来解决这个问题。 awk的功能比cut的功能要强大的多。…...
Java做新闻网站/百度竞价收费标准
做这题 来为上年 上海的那个状压bfs做铺垫 _ 这题 不难 就是 多开一维 存下 钥匙的状态就好了 表示在 (x,y)这个坐标点 已经收集到了哪些钥匙 多做点 状压的题目 就会来感觉的. 1 #include <iostream>2 #include <queue>3 #include <cstring>4 using namespa…...
有赞微商城官网登入/石家庄seo培训
一、检测Web异常操作 1.数据搜集:一样 2.特征化 使用词集模型,统计全部操作命令,去重后形成字典或词汇表: with open(filename) as f:for line in f:lineline.strip(\n)dist.append(line) fdist FreqDist(dist).keys() 以此词汇…...