【Tomcat】第五站:Servlet容器

news2025/2/28 6:39:57

        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之后,通过浏览器输入请求,得到信息。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2261096.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

猫眼浏览器v4.12.1重磅升级,极速、安全、保护全方位提升

猫眼浏览器&#xff08;Catsxp&#xff09;是一款经过深度优化的网页浏览器&#xff0c;凭借其创新性的设计理念和卓越的性能表现&#xff0c;正在重新定义现代浏览器的标准。这款基于Chrome内核精心打造的浏览器不仅为用户带来了全方位的网络体验提升&#xff0c;更在保护用户…

Axure原型设计技巧与经验分享

AxureRP作为一款强大的原型设计工具&#xff0c;凭借其丰富的交互设计能力和高保真度的模拟效果&#xff0c;赢得了众多UI/UX设计师、产品经理及开发人员的青睐。本文将分享一些Axure原型设计的实用技巧与设计经验&#xff0c;帮助读者提升工作效率&#xff0c;打造更加流畅、用…

梳理你的思路(从OOP到架构设计)_UML应用:业务内涵的分析抽象表达02

目录 1、举例(二) &#xff1a;西方童话 童话『青蛙王子』 的故事 故事中的概念 2、举例(三) &#xff1a;点餐服务 『 餐馆点餐服务』 的分析步骤&#xff1a; 1、举例(二) &#xff1a;西方童话 童话『青蛙王子』 的故事 从前有一位美丽的公主&#xff0c;喜欢玩金球。…

DevExpress WPF中文教程:Grid - 如何移动和调整列大小?(一)

DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序&#xff0c;这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…

OpenIPC开源FPV之Adaptive-Link天空端代码解析

OpenIPC开源FPV之Adaptive-Link天空端代码解析 1. 源由2. 框架代码3. 报文处理3.1 special报文3.2 普通报文 4. 工作流程4.1 Profile 竞选4.2 Profile 研判4.3 Profile 应用 5. 总结6. 参考资料7. 补充资料7.1 RSSI 和 SNR 的物理含义7.2 信号质量加权的理论依据7.3 实际应用中…

23.DDD与微服务

学习视频来源&#xff1a;DDD独家秘籍视频合集 https://space.bilibili.com/24690212/channel/collectiondetail?sid1940048&ctype0 文章目录 DDD与微服务的关系1. DDD可以用微服务实现&#xff0c;也可以不用微服务实现2. DDD是微服务拆分的必须参考项之一3. 微服务架构…

最新全开源IM即时通讯系统源码(PC+WEB+IOS+Android)部署指南

全开源IM&#xff08;即时通讯&#xff09;系统源码部署是一个复杂但系统的过程&#xff0c;涉及多个组件和步骤。以下是一个详细的部署指南&#xff0c;旨在帮助开发者或系统管理员成功部署一个全开源的IM系统&#xff0c;如OpenIM。      IM即时通讯系统源码准备工作   …

PVE系统下——OpenWRT一键扩容脚本(x86下EXT4SquashFS)

扩容了x86上的 OpenWrt 根分区和文件系统。 1.PVE 上增加硬盘大小 2.执行脚本 安装依赖 opkg update opkg install parted losetup resize2fs下载脚本并一键执行 wget -U "" -O expand-root.sh "https://openwrt.org/_export/code/docs/guide-user/advanced…

Midjourney教程之生成同一角色的不同姿势和服装

今天给大家介绍的是在 Midjourney 中如何创建同一个角色的不同姿势。这个功能是大家期待已久的&#xff0c;现在它已经正式可用了。 这个功能能够创建与原始图像相似的角色。"Character Reference" 功能类似于 "Style Reference"&#xff0c;但侧重于角色…

gitlab仓库API操作

几年没接触gitlab了&#xff0c;新版本装完以后代码提交到默认的main分支&#xff0c;master不再是主分支 项目有几十个仓库&#xff0c;研发提交代码后仓库地址和之前的发生了变化 先修改Group的默认分支&#xff0c;不会影响已存在的项目 修改gitlab全局的默认分支 这就引…

aws(学习笔记第十七课) SQS Amazon Simple Queue Service服务

aws(学习笔记第十七课) SQS Amazon Simple Queue Service服务 学习内容&#xff1a; 使用SQS Amazon Simple Queue Service服务整体代码&#xff08;nodejs的通常工程&#xff09;代码动作 1. 使用SQS Amazon Simple Queue Service服务 利用应用程序来学习SQS 创建S3$ aws s…

Swin Transformer:用Transformer实现CNN多尺度操作

文本是关于Swin Transformer基础知识的了解 论文&#xff1a;https://arxiv.org/pdf/2103.14030 项目&#xff1a;https://github. com/microsoft/Swin-Transformer. 实现一个Swin Transformer&#xff1a;Swin Transformer模型具体代码实现-CSDN博客 Swin Transformer mlp…

linux学习笔记02 linux中的基础设置(修改主机名、ip、防火墙、网络配置管理)

目录 修改主机名 ​编辑 修改ip地址 防火墙 关闭networkmanage 修改主机名 查看主机名 hostnamectl status 修改主机名 vim /etc/hostname 修改ip地址 vim /etc/sysconfig/network-scripts/ifcfg-ens33 输入这个命令后对照以下文件修改 TYPE"Ethernet" PROXY_M…

Windows 系统中的组策略编辑器如何打开?

组策略是 Windows 操作系统中用于设置计算机和用户配置的重要工具。它允许管理员控制各种系统功能&#xff0c;从桌面背景到安全设置等。对于 Windows 专业版、企业版和教育版用户来说&#xff0c;可以通过组策略编辑器&#xff08;Group Policy Editor&#xff09;来管理这些设…

【Go】Linux、Windows、Mac 搭建Go开发环境

1、Linux 第一步&#xff0c;在 官网 下包&#xff0c;如 go1.23.4.linux-386.tar.gz&#xff08;注意架构区分&#xff09; 第二步&#xff0c;将包上传至服务器&#xff0c;假如上传到 tmp目录下第三步&#xff0c;安装# 解压 tar -C /app -xzvf go1.23.4.linux-386.tar.gz#…

那一抹暖色

上海这周都是阳光明媚的天气&#xff0c;趁着工作日人少&#xff0c;来到公园看&#x1f341; 圣诞临近&#xff0c;一到这个节日&#xff0c;上海几乎一个月前&#xff0c;街上就有&#x1f385;&#x1f3fb;出没。 就先发这些吧&#xff0c;视频明天再做&#xff0c;眼睛要睁…

计算机毕业设计Python+Django农产品推荐系统 农产品爬虫 农产品商城 农产品大数据 农产品数据分析可视化 PySpark Hadoop

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

系统思考—沟通成本

昨天我们提到&#xff0c;企业真正的“降本”是减少决策错误的成本&#xff0c;今天我们来聊聊另一个重要的“成本”——沟通成本。这其实是一个典型的系统思考问题&#xff0c;沟通成本高并不是孤立存在的&#xff0c;而是系统中多种因素的结果。 1、层级多&#xff0c;信息损…

day11 性能测试(4)——Jmeter使用(黑马的完结,课程不全)直连数据库+逻辑控制器+定时器

【没有所谓的运气&#x1f36c;&#xff0c;只有绝对的努力✊】 目录 1、复习 1.1 断言&#xff08;3种&#xff09; 1.2 关联&#xff08;3种&#xff09; 1.3 录制脚本 2、Jmeter直连数据库 2.1 直连数据库——使用场景 2.2 直连数据库——操作步骤 2.2.1 案例1&…

禅道Bug的一次迁移

一、场景 平时工作记录在公司禅道上的问题想备份一份到本地&#xff0c;但是又没有公司禅道的数据库信息&#xff0c;有时候出测试报告想批量调整数据方便截图很困难&#xff0c;同时也为了学习禅道数据流转过程&#xff0c;所以有了把缺陷保存到本地一份的想法。 实际上禅道支…