【Tomcat】第五站:Servlet容器
Tomcat启动后,获取到项目当中所有的servlet的@WebServlet中的配置信息。将配置信息和类对象都写入一个map集合当中。

map就是一个key-value类型的集合。
在MyTomcat中我们获取到了类对象和注解值。
Tomcat与请求连通
1. ServletConfigMapping
1. 创建一个config包,包下新建一个ServletConfigMapping类,写一个static代码块,static代码块在main方法执行之前执行。把MyTomcat中的代码搬过来。
package com.qcby.tomcat.config;import com.qcby.tomcat.webServlet.WebServlet;import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;/*
* servlet容器
*
*
* */
public class ServletConfigMapping {//static代码块在main方法执行之前执行static{try {// 1. 扫描包路径 ()String packageName = "com.qcby.tomcat.myweb";//通过调用getclass方法,获取到了myweb这个包下的所有类的类对象,并将其放入到了容器当中List<Class<?>> classes = getClasses(packageName);// 2. 遍历所有类,检查是否有@WebServlet注解for (Class<?> clazz : classes) {if (clazz.isAnnotationPresent(WebServlet.class)) {//检查是否含有@WebServlet注解// 3. 获取@WebServlet注解的值WebServlet webServlet = clazz.getAnnotation(WebServlet.class);System.out.println("类名: " + clazz.getName() + " | URL路径: " + webServlet.path());}}} catch (Exception e) {e.printStackTrace();}}/*** 获取指定包下的所有类** @param packageName 包名,例如 "com.qcby.tomcat.myweb"* @return 类对象列表* @throws Exception*/private static List<Class<?>> getClasses(String packageName) throws Exception {List<Class<?>> classes = new ArrayList<>();//定义一个可变长数组,放置类对象String path = packageName.replace('.', '/'); // 将包名转换为文件路径// 通过类加载器获取包的资源路径 ClassLoader是类加载器,负责动态加载Java类到Java虚拟机(JVM)中。ClassLoader classLoader = Thread.currentThread().getContextClassLoader();//Thread.currentThread() 方法返回对当前执行线程的引用。//.getContextClassLoader() 方法则返回该线程的上下文类加载器。上下文类加载器是线的程在创建时从父线程继承,// 或者在创建线程时没有设置父线程的上下文类加载器时,默认使用应用程序类加载器(Application ClassLoader)//ClassLoader 类提供了 getResources(String name) 方法,该方法用于---查找具有指定名称的资源Enumeration<URL> resources = classLoader.getResources(path);//Enumeration<URL> 对象包含了所有找到的资源的URL。while (resources.hasMoreElements()) {//通过hasMoreElements()方法来检查是否还有更多的元素如果返回true,则表示枚举中还有元素未被遍历,可以继续调用nextElement()方法来获取下一个元素URL resource = resources.nextElement();//调用URL对象的toURI()方法将其转换为URI对象。//使用new File(URI uri)构造函数将URI对象转换为File对象。File directory = new File(resource.toURI());//File对象代表目录,不是文件// 扫描文件夹下的所有类文件if (directory.exists()) {for (File file : directory.listFiles()) {//可以利用listFiles()方法来获取该目录下所有文件和子目录的数组if (file.getName().endsWith(".class")) {//java文件自己创建的话是xx.java文件,后经过编译是:xx.class文件,这里用的是编译后的文件// 获取类的完整类名String className = packageName + "." + file.getName().replace(".class", "");classes.add(Class.forName(className));}}}}return classes;}public static void main(String[] args) {}
}
执行这个main方法,就自动会先执行static代码块。通常是这样用的。(也可以直接写在main方法里)。

2. 创建map映射表,类对象用HttpServlet类型承接。
!!为什么这里是HttpServlet?
for (Class<?> clazz : classes) {if (clazz.isAnnotationPresent(WebServlet.class)) {//检查是否含有@WebServlet注解// 3. 获取@WebServlet注解的值WebServlet webServlet = clazz.getAnnotation(WebServlet.class);//System.out.println("类名: " + clazz.getName() + " | URL路径: " + webServlet.path());//classMap.put(webServlet.path(),(Class<HttpServlet>) clazz);} }这里的类对象:
MyFirstServlet的类对象
MySecondServlet的类对象
MyThirdServlet的类对象
这几个都继承了HttpServlet,都是HttpServlet的子类。
我们就可以用父类的容器去承接子类的对象,这样的话就可以承接这几个,如果写MyFirstServlet,就不能写其他的类,无法转型。父类的引用指向子类的对象,子类可以向上转型为父类的引用。他们之间没有父子类的关系。
故写HttpServlet。
代码修改:
public static Map<String,Class<HttpServlet>> classMap=new HashMap<>();
//static代码块在main方法执行之前执行
static{try {// 1. 扫描包路径 ()String packageName = "com.qcby.tomcat.myweb";//通过调用getclass方法,获取到了myweb这个包下的所有类的类对象,并将其放入到了容器当中List<Class<?>> classes = getClasses(packageName);// 2. 遍历所有类,检查是否有@WebServlet注解for (Class<?> clazz : classes) {if (clazz.isAnnotationPresent(WebServlet.class)) {//检查是否含有@WebServlet注解// 3. 获取@WebServlet注解的值WebServlet webServlet = clazz.getAnnotation(WebServlet.class);//System.out.println("类名: " + clazz.getName() + " | URL路径: " + webServlet.path());classMap.put(webServlet.path(),(Class<HttpServlet>) clazz);}}
2. MyTomcat
当执行MyTomcat的main方法时,ServletConfigMapping并没有执行。

启动和后两个没有连接上。
我们想要当启动Tomcat时,就能获取到项目当中所有的配置信息,根据生成类对象,并且把类对象放入到容器中。
即,想要当MyTomcat的main方法执行时,ServletConfigMapping也执行。

方法很简单,只需要在ServletConfigMapping写一个静态方法,什么名字都可以,在MyTomcat调用即可。
因为static代码块在main方法之前执行,所以在static代码块执行之后,定义的www方法(改名init())才会执行。
或者直接把static代码块中的代码放进init方法中。
3. Server
同样,在Server类中,把main方法改成serverInit方法(把该方法直接调到MyTomcat类中)
public static void serverInit() throws Exception{// 1.打开通信端口 tomcat:8080 3306 ---------》进行网络通信ServerSocket serverSocket = new ServerSocket(8080);//监听8080端口号System.out.println("****************server start.....");//2.接受请求数据while (true){Socket socket = serverSocket.accept(); //--------------------->注意:此时监听网卡的是:主线程System.out.println("有客户进行了链接");new Thread(()->{ //利用子线程方式处理数据//处理数据---------》数据的处理在于读和写try {handler(socket);} catch (Exception e) {e.printStackTrace();}}).start();}
}
然后,在MyTomcat中调用。
public class MyTomcat {public static void main(String[] args) {try{ServletConfigMapping.init();//初始化servlet容器Server.serverInit();//启动server服务}catch (Exception e){e.printStackTrace();}}


现在,请求也打过来了,该执行下一步,根据key值,获取类对象。

4. 如何做好匹配?
将请求的信息封装到了request对象当中,根据request当中的path,去解决掉当前匹配问题。
当程序启动之后,已经把map集合创建好了,此时用户的请求打过来了,请求信息处理完之后封装在request对象当中,
package com.qcby.tomcat;import com.qcby.tomcat.HttpServlet.HttpServlet;
import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.Response.Response;
import com.qcby.tomcat.config.ServletConfigMapping;
import com.qcby.tomcat.socket.Server;
import com.qcby.tomcat.webServlet.WebServlet;import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;public class MyTomcat {public static Request request=new Request();public static Response response=new Response();public static void main(String[] args) throws Exception {ServletConfigMapping.init();serverInit();}public static void serverInit() throws Exception{// 1.打开通信端口 tomcat:8080 3306 ---------》进行网络通信ServerSocket serverSocket = new ServerSocket(8080);//监听8080端口号System.out.println("****************server start.....");//2.接受请求数据while (true){Socket socket = serverSocket.accept(); //--------------------->注意:此时监听网卡的是:主线程System.out.println("有客户进行了链接");new Thread(()->{ //利用子线程方式处理数据//处理数据---------》数据的处理在于读和写try {handler(socket);} catch (Exception e) {e.printStackTrace();}}).start();}}public static void handler(Socket socket) throws Exception {//读取请求的数据InputStream inputStream = socket.getInputStream();requestContext(inputStream);}public static void requestContext(InputStream inputStream) throws IOException, InstantiationException, IllegalAccessException {//将bit流转为文字信息int count = 0;while (count == 0){count = inputStream.available();}byte[] bytes = new byte[count];inputStream.read(bytes);String Context = new String(bytes);System.out.println(Context);//解析数据if(Context.equals("")){System.out.println("你输入了一个空请求");}else {//获得第一行的前两个String firstLine=Context.split("\\n")[0];//根据换行来获取第一行数据String path=firstLine.split("\\s")[1];String method=firstLine.split("\\s")[0];System.out.println(path+" "+method);request.setMethod(method);request.setPath(path);}dis(request);}public static void dis(Request request) throws InstantiationException, IllegalAccessException {if(!request.getPath().equals("")){//不是空请求if(ServletConfigMapping.classMap.get(request.getPath())!=null){//当前请求地址能否从classMap中查找到Class<HttpServlet> ClassServlet=ServletConfigMapping.classMap.get(request.getPath());//获取类对象HttpServlet servlet=ClassServlet.newInstance();//根据获取到的类对象,创建对应的对象 用父类去接,多态(父类的引用指向子类的对象)servlet.service(request,response);//调用HttpServlet的service方法,判断是get请求还是post请求}}}}
其中,
public static void dis(Request request) throws InstantiationException, IllegalAccessException {if(!request.getPath().equals("")){//不是空请求if(ServletConfigMapping.classMap.get(request.getPath())!=null){//当前请求地址能否从classMap中查找到,匹配成功Class<HttpServlet> ClassServlet=ServletConfigMapping.classMap.get(request.getPath());//获取类对象HttpServlet servlet=ClassServlet.newInstance();//根据获取到的类对象,创建对应的对象 用父类去接,多态(父类的引用指向子类的对象)servlet.service(request,response);//调用HttpServlet的service方法,判断是get请求还是post请求}} }方法是对象当中的一块内存空间
但是这是类对象,不是对象,想要得到信息就得生成对象。
通过类对象去创建对象,是我们需要做的。
用类对象创建对象, 因为不确定是请求的哪个对象,需要用父类去承接(多态), 根据对象调用它的doGet或doPost方法。
如此,
即可在启动Tomcat之后,通过浏览器输入请求,得到信息。

相关文章:
【Tomcat】第五站:Servlet容器
Tomcat启动后,获取到项目当中所有的servlet的WebServlet中的配置信息。将配置信息和类对象都写入一个map集合当中。 map就是一个key-value类型的集合。 在MyTomcat中我们获取到了类对象和注解值。 Tomcat与请求连通 1. ServletConfigMapping 1. 创建一个config包…...
CTF 攻防世界 Web: FlatScience write-up
题目名称-FlatScience 网址 index 目录中没有发现提示信息,链接会跳转到论文。 目前没有发现有用信息,尝试目录扫描。 目录扫描 注意到存在 robots.txt 和 login.php。 访问 robots.txt 这里表明还存在 admin.php admin.php 分析 在这里尝试一些 sql…...
【SpringBoot中MySQL生成唯一ID的常见方法】
SpringBoot中MySQL生成唯一ID的常见方法 在Spring Boot中,为MySQL生成唯一ID有多种方式,每种方式都有其特定的概念、优越点和使用场景。以下是详细的说明和代码示例: UUID 概念: UUID(Universally Unique Identifier࿰…...
使用Flink CDC实现 Oracle数据库数据同步的oracle配置操作
使用Flink CDC实现 Oracle数据库数据同步的oracle配置操作,包括开启日志归档和用户授权。 flink官方参考资料: https://nightlies.apache.org/flink/flink-cdc-docs-master/zh/docs/connectors/flink-sources/oracle-cdc/ 操作步骤: 1.启用…...
c++作业7
模拟一个游戏场景 有一个英雄:初始所有属性为1 atk,def,apd,hp 游戏当中有以下3种武器 长剑Sword: 装备该武器获得 1atx,1def 短剑Blade: 装备该武器获得 1atk,1spd 斧头Axe: 装备该…...
vue 上传组件 vxe-upload 实现拖拽调整顺序
vue 上传组件 vxe-upload 实现拖拽调整顺序,通过设置 drag-sort 参数就可以启用拖拽排序功能 官网:https://vxeui.com/ 图片拖拽排序 <template><div><vxe-upload v-model"imgList" mode"image" multiple drag-sor…...
Windows 环境实战开源项目GFPGAN 教程
GFPGAN GFPGAN(Generative Facial Prior-GAN)是由腾讯ARC(Applied Research Center)开发的一种实用的真实世界人脸修复算法。它专门设计用于人脸图像的生成和优化,尤其在低质量人脸图像的超分辨率恢复方面表现出色。以…...
UE5 做简单的风景观光视频
A、思路 新建摄像机,关卡序列, 镜头试拍录制器,新建镜头轨道,拖入摄像机, 变换,设置多个关键帧,改变摄像机在场景中的位置, 完成后,导出即可。 B、参考图...
k8s服务搭建与实战案例
Kubernetes(K8s)作为一个开源的容器编排平台,广泛应用于现代的云原生应用架构中。以下是一些常见的 **Kubernetes 实战案例**,包括从基础部署到高级应用场景的使用。通过这些案例,可以更好地理解 K8s 的运作原理和最佳…...
JavaScript学习难点
一、语法的灵活性 动态类型: JavaScript 是一种动态类型语言,这意味着变量的类型可以在运行时改变。这与静态类型语言(如 Java、C)形成鲜明对比,在静态类型语言中,变量的类型在编译时就已经确定。 例如&am…...
Qt WORD/PDF(一)使用 QtPdfium库实现 PDF 预览
文章目录 一、简介二、下载 QtPdfium三、加载 QtPdfium 动态库四、Demo 使用 关于QT Widget 其它文章请点击这里: QT Widget 姊妹篇: Qt WORD/PDF(一)使用 QtPdfium库实现 PDF 操作 Qt WORD/PDF(二)使用 QtPdfium库实现…...
解决创建laravel项目,使用国外镜像超时,国内镜像缺包的问题
解决创建laravel项目,使用国外镜像超时,国内镜像缺包的问题 一、前言二、切换镜像三、创建最新版本四、创建指定版本 一、前言 最近想下载 laravel 框架看看,但也遇到了些麻烦,这里做个记录。 二、切换镜像 先查看镜像源&#…...
Java泛型设计详解
引言 在日常Java开发中,泛型是一个非常重要的特性。它提供了编译时的类型安全检查,增强了代码的可读性和可维护性。然而,对于初学者甚至一些有经验的开发者来说,泛型的使用和理解仍然是一个挑战。本文旨在深入探讨Java泛型的诞生…...
用ue5打开网址链接
需要用到 Launch URL 这个函数 字面意思就是打开填写的链接网页 这里填写的是百度,按下Tab键后就会打开百度的网页...
【大数据】-- 读放大和写放大
目录 一、定义 1. 读放大(Read Amplification) 定义 原因 优化方法 2. 写放大(Write Amplification) 定义 原因 优化方法 对比与联系 二、举例 1. Hadoop(HDFS) 读放大 写放大 2. Flink 读放大 写放大 3. Hive 读放大 写放大 4. Presto 读放大 写放…...
【前端】JavaScript 抽取字符串特定部分题目详解与实现思路
博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: 前端 文章目录 💯前言💯题目描述💯核心步骤与实现解析1. 分割字符串为数组(split 方法)2. 使用 filter 提取名字(偶数索引判断)3. 使…...
CNCF云原生生态版图-分类指南(一)- 观测和分析
CNCF云原生生态版图-分类指南(一)- 观测和分析 CNCF云原生生态版图-分类指南一、观测和分析(Observability and Analysis)(一)可观测性(Observablility)1. 是什么?2. 解决…...
热更新解决方案3 —— xLua
概述 xLua框架导入和AB包相关准备 xLua导入 其它的导入 C#调用Lua 1.Lua解析器 using System.Collections; using System.Collections.Generic; using UnityEngine; //引用命名空间 using XLua;public class Lesson1_LuaEnv : MonoBehaviour {// Start is called before the fi…...
如何让ai在游戏中更像一个人?
开题开了一整年是我没想到的,还因此延毕了……我重新梳理一下我想做的研究以及相关痕迹。 我2023年3月找到的导师。起初我发现了在玩RTS游戏中会出现很多固定的套路,选手为此要做大量的练习,我就在想如何把这部分内容借助状态机这种流程给…...
websocket_asyncio
WebSocket 和 asyncio 指南 简介 本指南涵盖了使用 Python 中的 websockets 库进行 WebSocket 编程的基础知识,以及 asyncio 在异步非阻塞 I/O 中的作用。它提供了构建高效 WebSocket 服务端和客户端的知识,以及 asyncio 的特性和优势。 1. 什么是 WebS…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...


