搭建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...
微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...
用鸿蒙HarmonyOS5实现中国象棋小游戏的过程
下面是一个基于鸿蒙OS (HarmonyOS) 的中国象棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chinesechess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├──…...
在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南
在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南 背景介绍完整操作步骤1. 创建Docker容器环境2. 验证GUI显示功能3. 安装ROS Noetic4. 配置环境变量5. 创建ROS节点(小球运动模拟)6. 配置RVIZ默认视图7. 创建启动脚本8. 运行可视化系统效果展示与交互技术解析ROS节点通…...
【Java基础】向上转型(Upcasting)和向下转型(Downcasting)
在面向对象编程中,转型(Casting) 是指改变对象的引用类型,主要涉及 继承关系 和 多态。 向上转型(Upcasting) ⬆️ 定义 将 子类对象 赋值给 父类引用(自动完成,无需强制转换&…...
如何使用CodeRider插件在IDEA中生成代码
一、环境搭建与插件安装 1.1 环境准备 名称要求说明操作系统Windows 11JetBrains IDEIntelliJ IDEA 2025.1.1.1 (Community Edition)硬件配置推荐16GB内存50GB磁盘空间 1.2 插件安装流程 步骤1:市场安装 打开IDEA,进入File → Settings → Plugins搜…...

