【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…...

YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...

【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL
ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...
6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙
Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...