Tomcat原理(4)——尝试手动Servlet的实现

目录

一、什么是Servlet

1.servlet的定义

2.servlet的结构

二、实现servlet的流程图

三、具体实现代码

1、server

2.实体类request&response

3.HttpServlet抽象类

4.再定义三个servlet进行测试


Tomcat原理(3)------静&动态资源以及运行项目的基本流程-CSDN博客文章浏览阅读414次,点赞2次,收藏2次。Tomcat原理(2)------注解及注解的实现-CSDN博客(1)注解一般用于对程序的说明,就像注释一样,但是区别是注释是给人看的,但是注解是给程序看的。(2)让编译器进行编译检查的作用,比如下边这个@Override注解是重写的意思,子类重写了父类的方法,但是改动了方法名,所以报错。https://blog.csdn.net/2301_78566776/article/details/144508887?spm=1001.2014.3001.5502 我们在上一篇博客中已经了解到了静动态资源和tomcat的基本流程

一、什么是Servlet

1.servlet的定义

Servlet,全称为Java Servlet,是运行在Java服务器端的程序,它主要用于接收和响应来自客户端基于HTTP协议的请求。Servlet可以看作是在服务器上运行的小程序,用于生成动态Web内容。

2.servlet的结构

这是一个servlet的基本结构。我们观察可以发现:

  • Servlet继承了一个HttpServlet抽象类
  • servlet文件包含两个主要的方法:doGet和doPost
  • 观察这两个方法的入参一个是request 一个是response

二、实现servlet的流程图

1.我们首先需要构造一个Server服务器模拟,来接受HTTP请求,并且返回method(get/post)

2.创建request实体类和respons实体类,在request实体类中设置method和path变量

3.创建HttpServlet抽象类,在类里定义doGet和doPost两个抽象方法,并且判断所传method为什么类型,进行选择执行。

4.具体执行的doGet和doPost方法,要在具体的servlet中执行(找抽象类的具体实现方法)------即我们每一个servlet都要对doGet和doPost进行重写。

如图所示

三、具体实现代码

引入

我们尝试接收整个http请求的信息

java 复制代码
package com.qcby.tomcat.webservlet;/*
* 服务器端 tomcat ------》接收信息
* */

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class SocketServer {
    public static void main(String[] args) throws IOException {
        run();
    }

    public static void run() throws IOException {
        ServerSocket serverSocket= new ServerSocket(8080);
        //端口的范围:0~65535
        while (true){//等待客户端连接
            Socket socket = serverSocket.accept();//阻塞监听:程序会在这里卡住。只有监听到客户端的信息后才会向下执行
            //输出客户端给我们发来的程序
            InputStream inputStream=socket.getInputStream();//打开输入流:接收输入的信息
            int count=0;
            while (count==0){
                count=inputStream.available();
            }
            byte[] bytes=new byte[count];//01010101010100001101010 用字节数组接收
            inputStream.read(bytes);
            String context=new String(bytes);
            System.out.println(context);


        }
    }
}

结果如图

我们可以看到这个请求的全部信息。第一行的前两个词为其method/path,在一会的server文件中我们只要前两个词。

1、server

这个类中我们获取到了http请求的method(get/post)和path(@WebServlet中的path)

获取的方法是对所有请求信息进行切割。并且在最后我们将method和path放入了request中

java 复制代码
package com.qcby.tomcat.socket;

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 {
    private 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 {
        // 创建一个StringBuilder对象,用于构建请求的第一行
        StringBuilder sb = new StringBuilder();
        int context; // 用于存储每次从输入流中读取的单个字节

        // 读取输入流直到遇到换行符(\n)或文件结束(-1)
        while ((context = inputStream.read()) != -1) {
            // 如果读取到换行符,则停止读取
            if (context == '\n') {
                break; // 遇到换行符,退出循环
            }
            // 将读取到的字节转换为字符,并添加到StringBuilder中
            sb.append((char) context);
        }

        // 从StringBuilder中获取第一行字符串,并去除首尾空格
        String firstLine = sb.toString().trim();

        // 检查第一行是否为空
        if (firstLine.isEmpty()) {
            // 如果为空,则打印提示信息
            System.out.println("你输入了一个空请求");
        } else {
            // 如果不为空,则按空格分割第一行字符串为单词数组
            String[] words = firstLine.split("\\s+");
            // 打印出请求方法和请求URI(通常是数组的前两个元素)
            // 注意:这里没有检查数组长度,如果数组长度小于2,将会抛出ArrayIndexOutOfBoundsException
            // 在实际应用中,应该添加适当的错误处理或验证逻辑
            String method=words[0];
            String path=words[2];
            System.out.println(words[0] + " " + words[1]);
            request.setMethod(method);
            request.setPath(path);
        }

    }
}

@WebServlet接口

java 复制代码
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;

@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface WebServlet {

    String path() default "";
}
2.实体类request&response

生成了get和set方法

生成了全参和无参的构造函数

java 复制代码
package com.qcby.tomcat.Request;

public class Request {
    private String path;
    private String method;

    public Request(String path, String method) {
        this.path = path;
        this.method = method;
    }

    public Request() {

    }

    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;
    }
}

在这里 response我并没有实现

java 复制代码
package com.qcby.tomcat.Response;

public class Response {
}
3.HttpServlet抽象类

抽象类中既可以有抽象方法也可以有具体方法。

java 复制代码
package com.qcby.tomcat.HttpServlet;

import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.Response.Response;

public abstract class HttpServlet {

    public  void service(Request request, Response response){
        if(request.getMethod().equals("GET")){
            doGet(request,response);
        }else if (request.getMethod().equals("POST")){
            doPost(request,response);
        }else if (request.getMethod().equals("")){
            System.out.println("未获取到方法类型");
        }
    }
    public abstract void doGet(Request request,Response response);

    public abstract void doPost(Request request,Response response);


}
4.再定义三个servlet进行测试

注意!每个servlet都有继承HttpServlet,并且在每个servlet中都要重写doGet和doPost

java 复制代码
package com.qcby.tomcat.MyWeb;

import com.qcby.tomcat.HttpServlet.HttpServlet;
import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.Response.Response;
import com.qcby.tomcat.webservlet.WebServlet;

@WebServlet(path ="myFirstServlet")
public class MyFirstServlet extends HttpServlet {


    @Override
    public void doGet(Request request, Response response) {

    }

    @Override
    public void doPost(Request request, Response response) {

    }
}
java 复制代码
package com.qcby.tomcat.MyWeb;

import com.qcby.tomcat.HttpServlet.HttpServlet;
import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.Response.Response;
import com.qcby.tomcat.webservlet.WebServlet;

@WebServlet(path ="mySecondServlet")
public class MySecondServlet extends HttpServlet {

    @Override
    public void doGet(Request request, Response response) {

    }

    @Override
    public void doPost(Request request, Response response) {

    }
}
java 复制代码
package com.qcby.tomcat.MyWeb;

import com.qcby.tomcat.HttpServlet.HttpServlet;
import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.Response.Response;
import com.qcby.tomcat.webservlet.WebServlet;

@WebServlet(path ="myThirdServlet")
public class MyThirdServlet extends HttpServlet {

    @Override
    public void doGet(Request request, Response response) {

    }

    @Override
    public void doPost(Request request, Response response) {

    }
}

我们会发现它已经很像Java的servlet文件模板了

补充:

如何读取一个软件包,遍历里面的servlet,返回其类名和注解的path值

java 复制代码
package com.qcby.tomcat;

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;

public class MyTomcat {
    public static void main(String[] args) {
        try {
            // 1. 扫描包路径 (com.wzh.tomcat.myweb)
            String packageName = "com.qcby.tomcat.MyWeb";
            List<Class<?>> classes = getClasses(packageName);

            // 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.path());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取指定包下的所有类
     *
     * @param packageName 包名,例如 "com.wzh.tomcat.myweb"
     * @return 类对象列表
     * @throws Exception
     */
    private static List<Class<?>> getClasses(String packageName) throws Exception {
        List<Class<?>> classes = new ArrayList<>();
        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")) {
                        // 获取类的完整类名
                        String className = packageName + "." + file.getName().replace(".class", "");
                        classes.add(Class.forName(className));
                    }
                }
            }
        }
        return classes;
    }
}
相关推荐
FZUGO1 分钟前
EE308FZ_Sixth Assignment_Beta Sprint_Sprint Essay 3
java·微信小程序·sprint
阿髙2 分钟前
docker 软连接修改存储位置
java·docker·eureka
孤寂大仙v3 分钟前
【C++】智能指针详解
java·开发语言·c++
lzhdim10 分钟前
2、C#基于.net framework的应用开发实战编程 - 设计(二、二) - 编程手把手系列文章...
开发语言·c#·.net
山山而川粤21 分钟前
时间管理系统|Java|SSM|JSP|
java·开发语言·后端·学习·mysql
冒泡P23 分钟前
【Lua热更新】上篇
开发语言·数据结构·unity·c#·游戏引擎·lua
Q_192849990629 分钟前
基于Spring Boot的远程教育网站
java·spring boot·后端
我不想写昵称38 分钟前
【基础篇】1. JasperSoft Studio编辑器与报表属性介绍
java·后端·报表·jasperreport
爱学测试的李木子1 小时前
性能】JDK和Jmeter的安装与配置
java·开发语言·软件测试·测试工具·jmeter
百年孤独_1 小时前
高阶:基于Python paddleocr库 提取pdf 文档高亮显示的内容
开发语言·python·pdf