搭建Tomcat(三)---重写service方法
目录
引入
一、在Java中创建一个新的空项目(初步搭建)
问题:
要求在tomcat软件包下的MyTomcat类中编写main文件,实现在MyTomcat中扫描myweb软件包中的所有Java文件,并返回“@WebServlet(url="myFirst")”中url内填写的值:
①main函数解析:
首先,main函数用try-catch做了异常处理:
指定包名:
获取包下所有类的类对象:
扫描遍历:
二、搭建服务器端
在socket软件包的Server文件中编写:当存在客户端连接后,获取:
分析一下获取路径和请求方法的过程:
三、搭建
重写service方法:
引入
前面已经提到了,TomCat就是项目运行的环境,之前用到的Servlet文件都是通过eclipse中的tomcat容器来运行的,那么接下来在Java文件中去模拟这个过程。
在tomcat项目中创建Servlet项目。
一、在Java中创建一个新的空项目(初步搭建)
创建新项目后再去在目录下创建如下软件包和Java类。
@WebServlet接口中代码:
package com.qcby.tomcat.webservlet;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(value= RetentionPolicy.RUNTIME)
@Target(value={ElementType.TYPE})
public @interface WebServlet {String url() default "";String className() default "";
}
myweb里面的MyFirstServlet等三个文件内调用@WebServlet接口,如下代码:
(以MyFirstServlet为例:)
package com.qcby.tomcat.myweb;import com.qcby.tomcat.HttpServlet.HttpServlet;
import com.qcby.tomcat.webservlet.WebServlet;@WebServlet(url="myFirst")
public class MyFirstServlet extends HttpServlet {}
问题:
要求在tomcat软件包下的MyTomcat类中编写main文件,实现在MyTomcat中扫描myweb软件包中的所有Java文件,并返回“@WebServlet(url="myFirst")”中url内填写的值:
MyTomcat内:
package com.qcby.tomcat;import com.qcby.tomcat.webservlet.WebServlet;import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;public class MyTomcat {public static void main(String[] args) {//扫描myweb这个包下的所有文件,并获取到它的@WebServlet中的url的值// get current dirtry {// 1. 扫描包路径 (com.wzh.tomcat.myweb)String packageName = "com.qcby.tomcat.myweb";List<Class<?>> classes = getClasses(packageName); //通过getClasses()方法获取到了myweb这个包下面的所有类的类对象,并将其放到了类对象中// 2. 遍历所有类,检查是否有@WebServlet注解for (Class<?> clazz : classes) {if (clazz.isAnnotationPresent(WebServlet.class)) {// 3. 获取@WebServlet注解的值WebServlet webServlet = clazz.getAnnotation(WebServlet.class);System.out.println("类名: " + clazz.getName() + " | URL路径: " + webServlet.url());}}} 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<>(); //将类文件封装进List中String path = packageName.replace('.', '/'); // 将包名转换为文件路径// 通过类加载器获取包的资源路径ClassLoader classLoader = Thread.currentThread().getContextClassLoader();Enumeration<URL> resources = classLoader.getResources(path);while (resources.hasMoreElements()) {URL resource = resources.nextElement();File directory = new File(resource.toURI());// 扫描文件夹下的所有类文件if (directory.exists()) {for (File file : directory.listFiles()) {if (file.getName().endsWith(".class")) { //获得.class文件(.java->.class)此处获取的就是经过编译后的.class文件// 获取类的完整类名String className = packageName + "." + file.getName().replace(".class", "");classes.add(Class.forName(className));}}}}return classes;}
}
解析:
①main函数解析:
首先,main函数用try-catch做了异常处理:
try-catch
块用于捕获并处理在扫描包和读取注解时可能发生的异常。
指定包名:
String packageName = "com.qcby.tomcat.myweb";
:定义了一个字符串变量packageName
,它存储了要扫描的包名。
获取包下所有类的类对象:
List<Class<?>> classes = getClasses(packageName);
- 这行代码调用了一个名为
getClasses
的方法(后面分析getClasses就知道这是一个获取类对象的方法
),将这些获取的类对象写入到一个泛型中,方便后续遍历。- 调用
getClasses
方法,传入包名,获取该包下所有类的Class
对象列表,并存储在classes
变量中。
扫描遍历:
对获得并写入List中的每个文件进行
isAnnotationPresent判断是否有@WebServlet注解:
【注释:isAnnotationPresent
是Java语言中的一种方法,它主要用于判断某个类、方法、变量等元素上是否存在指定类型的注解。】
- 调用
clazz.getAnnotation(WebServlet.class);
时,Java虚拟机(JVM)会做以下几件事情:
- 检查
clazz
对象所代表的类上是否存在WebServlet
注解的实例。- 如果存在,返回这个注解的实例。
- 如果不存在,返回
null
。
扫描获得后打印输出。
那么分析完成main函数后,接着来分析一下getClases方法具体是怎么实现获取包中类信息的:
(详细参见注释:)
// 定义一个静态方法,用于获取指定包名下的所有类对象列表
private static List<Class<?>> getClasses(String packageName) throws Exception {List<Class<?>> classes = new ArrayList<>(); // 初始化一个ArrayList,用于存储找到的类对象// 将包名中的点(.)替换为文件路径中的斜杠(/),以构造资源路径String path = packageName.replace('.', '/');// 获取当前线程的类加载器,用于加载资源ClassLoader classLoader = Thread.currentThread().getContextClassLoader();// 通过类加载器获取指定路径下的所有资源(可能是目录或JAR文件中的条目)Enumeration<URL> resources = classLoader.getResources(path);// 遍历所有找到的资源while (resources.hasMoreElements()) {URL resource = resources.nextElement(); // 获取下一个资源URL// 注意:这里假设资源是一个文件系统上的目录,但这不是总是正确的,特别是当资源在JAR中时File directory = new File(resource.toURI()); // 将资源URL转换为File对象(这里存在潜在问题,对于JAR文件不适用)// 检查目录是否存在(对于文件系统上的资源有效)if (directory.exists()) {// 遍历目录下的所有文件和子目录for (File file : directory.listFiles()) {// 检查文件是否以".class"结尾,即是否是类文件(.java文件经过编译后的.class文件)if (file.getName().endsWith(".class")) {// 构造类的完整名称(包括包名)String className = packageName + "." + file.getName().replace(".class", "");// 使用反射加载类,并添加到类列表中// 注意:这里可能会抛出ClassNotFoundException,但在这个方法内部没有捕获处理classes.add(Class.forName(className));}}}}// 返回找到的类对象列表return classes;
}
二、搭建服务器端
在socket软件包的Server文件中编写:
当存在客户端连接后,获取:
package com.qcby.tomcat.socket;import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) throws Exception {// 1.打开通信端口 tomcat:8080 3306 ---------》进行网络通信ServerSocket serverSocket = new ServerSocket(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 { //获取全部信息//将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);}}//获取第一个词和第二个词
// public static void requestContext(InputStream inputStream) throws IOException {
// StringBuilder sb = new StringBuilder();
// int ch;
// // 读取输入流直到遇到换行符或文件结束
// while ((ch = inputStream.read()) != -1) {
// if (ch == '\n') {
// break; // 遇到换行符,停止读取
// }
// sb.append((char) ch); // 将读取的字符添加到StringBuilder中
// }
//
// String firstLine = sb.toString().trim(); // 获取第一行并去除首尾空格
// if (firstLine.isEmpty()) {
// System.out.println("你输入了一个空请求");
// } else {
// String[] words = firstLine.split("\\s+"); // 使用正则表达式按空格分割单词
// if (words.length >= 2) {
// System.out.println("第一个词是:" + words[0]);
// System.out.println("第二个词是:" + words[1].substring(1));
// } else {
// System.out.println("第一行没有足够的词");
// }
// }
// }}
分析一下获取路径和请求方法的过程:
String firstLine = Context.split("\\n")[0];
- 这行代码将
Context
字符串按照换行符(\n
)进行分割,并取出分割后的第一个元素(即第一行)赋值给firstLine
。String path = firstLine.split("\\s")[1];
- 接着,这行代码将
firstLine
按照空白字符(包括空格、制表符等,\s
是一个正则表达式,用于匹配任何空白字符)进行分割,并取出分割后的第二个元素(索引为1)赋值给path
。这里假设路径是firstLine
中方法名后面的第一个元素。String method = firstLine.split("\\s")[0];
- 这行代码再次对
firstLine
按照空白字符进行分割,并取出分割后的第一个元素(索引为0)赋值给method
。这里假设方法名是firstLine
中的第一个元素。System.out.println(path + " " + method);
- 最后,这行代码打印出
path
和method
的值,但顺序是先打印path
,后打印method
,中间用空格分隔。
三、搭建
【注:接口--接口就是用来定义方法的;实现接口类就必须实现接口中的方法;
抽象类中可以有抽象方法,也可以有具体方法;抽象类实现了接口,那么抽象类可以有选择性的实现接口中的方法】
重写service方法:
HTTP请求:
请求方法--Get,Post....等(主要是Get\Post方式)
而上述实现service方法,就是为了获取请求方法并且判断是Get还是Post请求(然后将请求送到doGet()/doPost()方法中)
在HttpServlet包中的方法HttpServlet抽象类中重写service方法:
首先创建request:
public class Request {private String path;private String method;public String getPath() {return path;}public void setPath(String path) {this.path = path;}public String getMethod() {return method;}public void setMethod(String method) {this.method = method;}
}
并且修改Server来得到path和method:
import com.qcby.tomcat.Request.Request;import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class Server {//实例化Requestprivate static Request request = new Request();public static void main(String[] args) throws Exception {// 1.打开通信端口 tomcat:8080 3306 ---------》进行网络通信ServerSocket serverSocket = new ServerSocket(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 { //获取全部信息//将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 method = firstLine.split("\\s")[0];String path = firstLine.split("\\s")[1];System.out.println(method + " " + path);//任何请求都会被打到这个类中,随后就会被解析//将解析后的数据(method和path放进申请的static的Request实例中--再被运输给其他需要的地方)request.setMethod(method);request.setPath(path);}}
即可通过这种方式让HttpServlet中的service方法得到Http请求的key和请求方法:
import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.socket.Server;public abstract class HttpServlet {/** 一定不能是抽象方法因为HttpServlet是要被实现的* 根据用户的请求,来调用不用的方法去处理* GET请求 doGet()请求* POST请求 doPost()请求** *///重写service()方法public void service(Request request){//一定不能是抽象方法因为HttpServlet是要被实现的if(request.getMethod().equals("GET")){doGet(request);}else if(request.getMethod().equals("POST")){doPost(request);}}//去实现doGet--意味着所有继承HttpServlet抽象类的对象,都要去实现这两个方法public abstract void doGet(Request request);//去实现doPostpublic abstract void doPost(Request request);}
那么就意味着(继承抽象类的实例必须实现抽象类中的方法):
那么到这里,一个基础的、连贯的tomcat雏形就存在了:
四、Tomcat雏形
1.tomcat的server接收到一个请求:
发送请求:
2.被tomcat的server接收到:
3.此时server内部创建了一个static的Request实例,并被HttpServlet里面的service接收:
通过if-else if判断后,被送到对应的doGet或doPost方法:
由于HttpServlet是抽象类,所以所有继承这个抽象类的实例都要实现抽象类中的所有方法:
类似:
同时,由于doGet()和doPost()方法都是抽象方法,所以HttpServlet想要实现这些方法,就要去到各自的实例中,而这个实例究竟是哪一个,则就对应path中存的路径了。
以上就是一个基础的雏形(关于tomcat是如何接收并初步处理一个请求的)
相关文章:

搭建Tomcat(三)---重写service方法
目录 引入 一、在Java中创建一个新的空项目(初步搭建) 问题: 要求在tomcat软件包下的MyTomcat类中编写main文件,实现在MyTomcat中扫描myweb软件包中的所有Java文件,并返回“WebServlet(url"myFirst")”中…...

跟着AI 学AI开发二,本地部署自己的Chat GPT
这里要安装的是Open Web UI ,用一张架构图说明AI 前端与后端的关系。 之前的Python 的方法已经做过多次介绍,这里不做赘述。 顺序:1,Ollama。 2,Docker。 3,Open WebUI。 Ollama 安装下载地址࿱…...

XXE靶机漏洞复现通关
1.扫描XXE靶机的ip地址 将kali虚拟机和XXE靶机部署在同一局域网中,都采用NAT网络模式 搭建好后在kali终端中进行扫描XXE靶机的ip arp-scan -l 根据常识我们可以推断192.168.27.153为靶机的ip地址 2.访问靶机页面并扫描附录 进入页面后我们可以打开御剑扫描网页中…...

XS9922B 同轴RX芯片 四通道 多合一模拟高清解码器
XS9922B 是一款 4 通道模拟复合视频解码芯片,支持 HDCCTV 高清协议和 CVBS 标 清协议,视频制式支持 720P/1080P 高清制式和 960H/D1 标清制式。芯片将接收到的高清 模拟复合视频信号经过模数转化,视频解码以及 2D 图像处理之后,转…...

如何在谷歌浏览器中设置电子邮件通知
在现代互联网生活中,电子邮件已成为我们日常沟通的重要工具。为了更高效地管理邮件,您可以在谷歌浏览器中设置电子邮件通知。本文将详细介绍如何实现这一功能,并附带一些相关的Chrome使用技巧。(本文由https://chrome.google64.cn…...
利用Java获取淘宝商品详情API接口的深入指南引言
引言 在电商领域,数据的价值日益凸显,尤其是在淘宝这样的大型电商平台上。淘宝商品详情API接口允许开发者通过编程方式获取商品的详细信息,这对于市场分析、竞争对手研究等方面至关重要。本文将详细介绍如何使用Java编写爬虫程序,…...

3D工具显微镜的测量范围
一、测量尺寸范围 样品尺寸: 3D工具显微镜通常能够测量各种尺寸和形状的样品,从小至微米级别的微小结构到大至几厘米甚至更大的物体。具体的测量尺寸范围取决于显微镜的载物台大小、镜头焦距以及软件处理能力。测量精度: 3D工具显微镜的测量…...

WPF DataTemplate 数据模板
DataTemplate 顾名思义,数据模板,在 wpf 中使用非常频繁。 它一般用在带有 DataTemplate 依赖属性的控件中,如 ContentControl、集合控件 ListBox、ItemsControl 、TabControls 等。 1. 非集合控件中使用 <UserControl.Resources>&l…...

知道一个服务器IP地址,如何attack对方美国
CSDN提醒:亲爱的用户:你好! 你的账号于2024-12-17 19:04:04在美国美国登录,登录IP为:47.238.159.124。若非本人登录,请及时修改密码。 莫名其妙显示美国登录了我的CSDN博客 卧槽 简介 服务器的IP地址是一…...

lettuce 默认情况下连接池参数不生效,源码分析
先说结论: 1.LettuceConnectionFactory 属性 shareNativeConnection 默认为true,要想连接池生效,该参数设置为false; 2.使用redisTemplate模版封装的pipeline没有意义,autoFlashCommands 默认为true;spring2.0开始默认使用lettuc…...

《宇宙机器人》提示错误弹窗“找不到d3dx9_43.dll”是什么原因?“d3dx9_43.dll缺失”怎么解决?
电脑游戏运行时常见问题解析:《宇宙机器人》提示“找不到d3dx9_43.dll”的解决之道 TGA2024落幕,年度最佳游戏——《宇宙机器人》,作为一名在软件开发领域深耕多年的从业者,我深知电脑游戏在运行过程中可能会遇到的各种挑战&…...
应用于项目的 C++单例基类的设计、实现与应用
文章目录 应用于项目的 C单例基类的设计、实现与应用一、引言二、单例基类的设计2.1 线程安全的单例基类2.2 局部静态变量的单例基类 三、单例基类的实现3.1 配置管理单例类 四、单例基类的应用4.1 多线程环境下的配置管理 五、深入探讨5.1 单例的线程安全问题5.2 单例的延迟初…...
Mongodb 启用认证
MongoDB 启用认证的完整指南 启用 MongoDB 的认证功能需要按照以下步骤进行设置: 检查 MongoDB 配置文件 在 MongoDB 配置文件中(通常为 mongod.conf),需要启用认证功能。 修改配置文件 打开 mongod.conf 文件,找…...
QT:vlc出错处理及重新播放
这个问题一直想解决,昨天认真研究了一下。 要点 视频用的Widget不能重复使用,每次出错后,都要新建。 回调函数的处理。 代码1 关键在于libvlc_event_attach void VideoWidget::play() {libvlc_media_t* media;if (strstr(video_path, &…...

密钥管理系统在数据安全解决方案中的重要性
密钥管理系统在数据安全解决方案中占据着举足轻重的地位,其重要性体现在以下几个方面: 一、保障数据机密性 密钥管理系统通过生成、存储和管理加密密钥,确保了数据的机密性。这些密钥用于加密和解密数据,只有授权用户才能访问和…...

Docker的容器编排
目录 1. 什么是容器编排(Docker Compose)2. 容器编排的功能3. 容器编排文件(docker-compose.yml)的介绍3.1 文件语法版本3.2 文件基本结构及常见指令 4. Docker Compose命令详解4.1 Docker Compose命令清单4.2 命令格式和常见选项…...

Java Web项目部署教程简单实用
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默, 忍不住分享一下给大家。点击跳转到网站 学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……) 2、学会Oracle数据库入门到入土用法(创作中……) 3、手把…...

推送本地仓库到远程git仓库
目录 推送本地仓库到远程git仓库1.1修改本地仓库用户名1.2 push 命令1.3远程分支查看 推送本地仓库到远程git仓库 删除之前的仓库中的所有内容,从新建库,同时创建一个 A.txt 文件 清空原有的远程仓库内容,重新创建一个新的仓库,…...
线性池学习
一、什么是进程?什么是线程? 1. 进程的定义 从操作系统的角度解释: 进程是操作系统分配资源和调度执行的基本单位。每个进程都是操作系统中一个独立的实体,拥有自己的内存空间、文件描述符、代码、数据等资源。进程是程序在执行…...

微积分复习笔记 Calculus Volume 2 - 4.3 Separable Equations
4.3 Separable Equations - Calculus Volume 2 | OpenStax...

【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...

莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...

群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...