用BIO实现tomcat

一、前言

本课程的难度较高,需要将Servlet原理和IO课程全部学完。

二、当前项目使用方式

(1).自定义servlet

自定义servlet需要实现@WebServlet并且实现name和urlMapping
在这里插入图片描述
重启进行访问
http://localhost:8090/myServlet
在这里插入图片描述

(2).自定义html

在这里插入图片描述
重启进行访问
http://localhost:8090/index.html
在这里插入图片描述

(3).关于servlet位置

在这里插入图片描述
在SearchClassUtil类当中可以设置servlet包的位置

三、关于web须知

我们本次设计的tomcat能够将用户请求的资源进行返回

资源分类
1.静态资源:所有用户访问后,得到的结果都是一样的,称为静态资源。
           静态资源可以直接被浏览器解析。
           *例如:html/css/jpg/js..
2.动态资源:每个用户访问相同的资源后,得到的结果可能不一样,称为动态资源。
           动态资源被访问后需要先转化为静态资源,再返回给浏览器,浏览器进行解析
           *例如:servlet/jsp ...

四、tomcat设计原理

在这里插入图片描述

五、实现tomcat对静态资源的访问

(1).创建maven项目

在这里插入图片描述
在这里插入图片描述

(2).tomcat启动阶段

配置HttpServlet

创建HttpServletRequest接口

public interface HttpServletRequest {
    public String getUrl();


    public void setUrl(String url);

    public String getMethod();

    public void setMethod(String method);
}

创建HttpServletResponse接口

public interface HttpServletResponse {
    void write(String context) throws IOException;
}

创建HttpServlet

/**
 * class HttpServlet
 */
public abstract class HttpServlet {

    public abstract void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException;

    public abstract void doPost(HttpServletRequest request,HttpServletResponse response);

    /**
     * HttpServlet 实现service方法
     */
    public void service(HttpServletRequest request,HttpServletResponse response) throws IOException {
        if("GET".equals(request.getMethod())){
            doGet(request,response);
        }else if("POST".equals(request.getMethod())){
            doPost(request,response);
        }
    }

}

创建工具类,描述返回的信息

/**
 * 返回信息工具类
 */
public class ResponseUtil {

    public  static  final String responseHeader200 = "HTTP/1.1 200 \r\n"+
            "Content-Type:text/html \r\n"+"\r\n";

    public static String getResponseHeader404(){
        return "HTTP/1.1 404 \r\n"+
                "Content-Type:text/html \r\n"+"\r\n" + "404";
    }

    public static String getResponseHeader200(String context){
        return "HTTP/1.1 200 \r\n"+
                "Content-Type:text/html \r\n"+"\r\n" + context;
    }
}

配置注解信息

@Retention(RetentionPolicy.RUNTIME)  //注解的生命周期,运行期间保留
@Target(value = {ElementType.TYPE,ElementType.FIELD}) // 该注解作用在类上边
public @interface WebServlet {
    String urlMapping() default ""; //自定义的servlet路径
}

配置servlet容器

创建ServletConfig存储注解信息

/**
 * 注解上的信息
 */
public class ServletConfig {
    private String urlMapping; //2.url
    private String classpath; //3.类的全路径名

    public ServletConfig(String urlMapping,String classpath){
        this.classpath = classpath;
        this.urlMapping = urlMapping;
    }

    public String getUrlMapping() {
        return urlMapping;
    }

    public void setUrlMapping(String urlMapping) {
        this.urlMapping = urlMapping;
    }

    public String getClasspath() {
        return classpath;
    }

    public void setClasspath(String classpath) {
        this.classpath = classpath;
    }
}

工具类,获取servlet的全路径名

/**
 * 扫描指定包,获取该包下所有的类的全路径信息
 */
public class SearchClassUtil {
    public static  List<String> classPaths = new ArrayList<String>();

    public static List<String> searchClass(){
        //需要扫描的包名
        String basePack = "com.qcby.webapp";
        //将获取到的包名转换为路径
        String classPath = SearchClassUtil.class.getResource("/").getPath();
        basePack =  basePack.replace(".", File.separator);
        String searchPath = classPath + basePack;
        doPath(new File(searchPath),classPath);
        //这个时候我们已经得到了指定包下所有的类的绝对路径了。我们现在利用这些绝对路径和java的反射机制得到他们的类对象
        return classPaths;
    }

    /**
     * 该方法会得到所有的类,将类的绝对路径写入到classPaths中
     * @param file
     */
    private static void doPath(File file,String classpath) {
        if (file.isDirectory()) {//文件夹
            //文件夹我们就递归
            File[] files = file.listFiles();
            for (File f1 : files) {
                doPath(f1,classpath);
            }
        } else {//标准文件
            //标准文件我们就判断是否是class文件
            if (file.getName().endsWith(".class")) {
                String path = file.getPath().replace(classpath.replace("/","\\").
                                replaceFirst("\\\\",""),"").replace("\\",".").
                        replace(".class","");
                //如果是class文件我们就放入我们的集合中。
                classPaths.add(path);
            }
        }
    }

    public static void main(String[] args) {
        List<String> classes = SearchClassUtil.searchClass();
        for (String s: classes
             ) {
            System.out.println(s);
        }
    }
}

创建ServletConfigMapping生成servlet容器

/**
 *  servlet容器
 */
public class ServletConfigMapping {
    
    //定义一个集合用来存储自定义servlet的配置信息
    private static List<ServletConfig> configs = new ArrayList<>();
    //定义servlet容器
    public static Map<String,Class<HttpServlet>> classMap = new HashMap<>();

    //解析注解 ---- 为了实现当mytomcat类启动的时候就将webapp下边所有的类的注解信息获取到我们需要写一个static代码块
    static {
        //1.获取webapp包下有哪些类
        List<String> classPaths = SearchClassUtil.searchClass();
        //2.获取类的注解信息
        for (String classpath: classPaths) {
            try {
                //利用反射获取类的注解信息
                getMessage(classpath);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    //利用反射获取类的注解信息
    public static void getMessage(String classPath) throws ClassNotFoundException {
        Class clazz = Class.forName(classPath);
        //注解对象
        WebServlet webServlet = (WebServlet) clazz.getDeclaredAnnotation(WebServlet.class);
        //将解析的信息放入到集合当中
        configs.add(new ServletConfig(webServlet.urlMapping(),classPath));
    }

    //初始化类容器
    public static void initServlet() throws ClassNotFoundException {
        for (ServletConfig servletConfig: configs) {
            // 将servlet对象和 url请求地址放入到 map集合当中去
            classMap.put(servletConfig.getUrlMapping(), (Class<HttpServlet>) Class.forName(servletConfig.getClasspath()));
        }
    }
}

(3).接收前端请求

创建Request类接收前端数据并实现HttpServletRequest接口

public class Request implements HttpServletRequest {
    //请求的地址
    private String url;
    //请求的方式
    private String Method;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getMethod() {
        return Method;
    }

    public void setMethod(String method) {
        Method = method;
    }
}

创建Response类用来实现HttpServletResponse

public class Response implements HttpServletResponse {
    //输出流
    private OutputStream outputStream;

    public Response(OutputStream outputStream){
        this.outputStream = outputStream;
    }

    /**
     * 返回动态资源
     * @param context
     */
    public void write(String context) throws IOException {
        outputStream.write(context.getBytes());
    }

    /**
     * 返回静态资源
     */
    public void writeHtml(String path) throws Exception {
        String resourcesPath = FileUtil.getResoucePath(path);
        File file = new File(resourcesPath);
        if(file.exists()){
            //静态文件存在
            System.out.println("静态文件存在");
            FileUtil.writeFile(file,outputStream);
        }else {
            System.out.println("静态文件不存在");
            write(ResponseUtil.getResponseHeader404());
        }
    }
}

工具类获取静态资源

/**
 * 该类的主要作用是进行读取文件
 */
public class FileUtil {

    public  static  boolean witeFile(InputStream inputStream, OutputStream outputStream){
        boolean success = false ;
        BufferedInputStream bufferedInputStream ;
        BufferedOutputStream bufferedOutputStream;

        try {
            bufferedInputStream = new BufferedInputStream(inputStream);
            bufferedOutputStream = new BufferedOutputStream(outputStream);
            bufferedOutputStream.write(ResponseUtil.responseHeader200.getBytes());

            int count = 0;
            while (count == 0){
                count = inputStream.available();
            }
            int fileSize = inputStream.available();
            long written = 0;
            int beteSize = 1024;
            byte[] bytes = new byte[beteSize];
            while (written < fileSize){
                if(written + beteSize > fileSize){
                    beteSize = (int)(fileSize - written);
                    bytes = new byte[beteSize];
                }
                bufferedInputStream.read(bytes);
                bufferedOutputStream.write(bytes);
                bufferedOutputStream.flush();
                written += beteSize;
            }
            success = true;

        } catch (IOException e) {
            e.printStackTrace();
        }
        return success;
    }

    public static boolean writeFile(File file,OutputStream outputStream) throws Exception{
        return witeFile(new FileInputStream(file),outputStream);
    }


    public static String getResoucePath(String path){
        String resource = FileUtil.class.getResource("/").getPath();
        return resource + "\\" + path;
    }

}

获取输入流信息,获取访问方式和访问地址

public class MyTomcat {

    Request request = new Request();

    //启动tomcat主方法
    public void startUp() throws IOException, ClassNotFoundException {
        
        //1.定义socket对象,监听8080端口
        ServerSocket serverSocket = new ServerSocket(8080);
        while (true){
            Socket socket = serverSocket.accept();//等待接收 BIO
            System.out.println("有用户请求过来了.....");
            // 给每一个请求都开启一个线程处理信息
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        杜凯(socket);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

    }
    //2.创建出入流,读取用户请求信息
    public void 杜凯(Socket socket) throws Exception {
        //创建输入流
        InputStream inputStream = socket.getInputStream();
        //解析输入流
        getInputStream(inputStream);

        socket.close();
    }

    public void getInputStream(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]; //根据换行来获取第一行数据
            request.setUrl(firstLine.split("\\s")[1]);
            request.setMethod(firstLine.split("\\s")[0]);
        }

    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        MyTomcat myTomcat = new MyTomcat();
        myTomcat.startUp();
    }

}

加载tomcat启动配置,判断访问内容时否是静态资源

public class MyTomcat {

    Request request = new Request();

    /**
     * servlet分发器
     * @param request
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public void dispatch(Request request, Response response) throws Exception {
        //根据请求的信息来获取servlet类
        Class<HttpServlet> servletClass = ServletConfigMapping.classMap.get(request.getUrl());
        //真实的创建servlet对象
        if(servletClass !=null){
           HttpServlet servlet =  servletClass.newInstance();
           servlet.service(request,response);
        }else {
            response.write(ResponseUtil.getResponseHeader404());
        }
    }

    //启动tomcat主方法
    public void startUp() throws IOException, ClassNotFoundException {
        //加载servlet信息
        ServletConfigMapping.initServlet();

        //1.定义socket对象,监听8080端口
        ServerSocket serverSocket = new ServerSocket(8080);
        while (true){
            Socket socket = serverSocket.accept();//等待接收 BIO
            System.out.println("有用户请求过来了.....");
            // 给每一个请求都开启一个线程处理信息
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        杜凯(socket);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

    }
    //2.创建出入流,读取用户请求信息
    public void 杜凯(Socket socket) throws Exception {
        //创建输入流
        InputStream inputStream = socket.getInputStream();
        //解析输入流
        getInputStream(inputStream);

        //输出流
        Response response = new Response(socket.getOutputStream());

        //根据url判断是静态资源还是动态资源
        if(request.getUrl().equals("")){
            //没有访问数据
            response.write(ResponseUtil.getResponseHeader404());
        }else if(ServletConfigMapping.classMap.get(request.getUrl()) == null){
            //访问静态资源
            response.writeHtml(request.getUrl());
        }else {
            //访问动态资源
            dispatch(request,response);
        }

        socket.close();
    }

    public void getInputStream(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]; //根据换行来获取第一行数据
            request.setUrl(firstLine.split("\\s")[1]);
            request.setMethod(firstLine.split("\\s")[0]);
        }

    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        MyTomcat myTomcat = new MyTomcat();
        myTomcat.startUp();
    }

}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

幂等性设计

目录 前言 幂等性设计 幂等性设计处理流程 HTTP 幂等性 消息队列幂等性 基于kafka 前言 幂等性设计&#xff0c;就是说&#xff0c;一次和多次请求某一个资源应该具有同样的副作用。为什么我们要有幂等性操作&#xff1f;说白了&#xff0c;就两点&#xff1a;1、网络的…

Vue-03

Vue指令 v-bind 作用&#xff1a;动态设置html的标签属性&#xff08;src url title…&#xff09; 语法&#xff1a;v-bind:属性名"表达式" 举例代码如下&#xff1a; 实现效果如下&#xff1a; 案例&#xff1a;图片切换 实现代码如下&#xff1a; 实现的效果…

072:vue+cesium 实现下雪效果

第072个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中实现下雪效果,这里使用着色器来实现实例特效。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共120行)着色代码实现心得:专栏目标示例效果

基于springboot+vue的健身房管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

JavaScript基础1之变量的var、const、let和数据类型的原始类型、对象类型、内存空间、拷贝

JavaScript基础 变量var关键字var声明的作用域var 定义多个变量变量提升 let关键字暂时性死区let不是Windows的属性 const关键字 数据类型原始类型对象类型内存空间拷贝拷贝原始类型拷贝引用数据类型比较 变量 ECMAScript变量&#xff1a;松散类型的 >变量可以保存任何类型…

EdgeX Foundry 安装部署

文章目录 一、概述1.官方文档2.Docker Compose 生成器3.创建 docker-compose 文件 二、安装准备1. 克隆服务器2.安装 Docker3.安装 docker-compose 三、非安全模式部署1.docker-comepse2.启动 EdgeX Foundry3.访问 UI3.1. consul3.2. EdgeX Console EdgeX Foundry # EdgeX Fou…

AI:144-通过机器学习预测股票市场趋势

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带关键代码,详细讲解供大家学习,希望…

IO 与 NIO

优质博文&#xff1a;IT-BLOG-CN 一、阻塞IO / 非阻塞NIO 阻塞IO&#xff1a;当一条线程执行read()或者write()方法时&#xff0c;这条线程会一直阻塞直到读取到了一些数据或者要写出去的数据已经全部写出&#xff0c;在这期间这条线程不能做任何其他的事情。 非阻塞NIO&…

大数据技术(一)

大数据技术概述 大数据技术层面及其功能 数据采集与预处理 利用ETL(extract-transform-load)工具将分布的、异构数据源中的数据&#xff0c;如关系数据、平面数据文件等&#xff0c;抽取到临时中间层后进行清洗、转换、集成&#xff0c;最后加载到数据仓库或数据集市中&…

单例模式及应用场景

如果希望自己的代码更优雅、可维护性更高以及更简洁&#xff0c;往往离不开设计模式这一解决方案。 在JS设计模式中&#xff0c;最核心的思想&#xff1a;封装变化&#xff08;将变与不变分离&#xff0c;确保变化的部分灵活&#xff0c;不变的部分稳定&#xff09;。 那么来…

RISC-V特权架构 - CSR寄存器

RV32/64 特权架构 - CSR寄存器 1 CSR地址空间2 CSR定义2.1 用户级2.2 监管级2.3 超级监管级2.4 机器级 3 CSR访问3.1 CSRRW3.2 CSRRS3.3 CSRRC3.4 CSRRWI3.5 CSRRSI3.6 CSRRCI 本文属于《 RISC-V指令集基础系列教程》之一&#xff0c;欢迎查看其它文章。 1 CSR地址空间 RISC&…

[笔记] 使用 Java Swing 实现一个简单的窗口

Java Swing 是一个用于构建图形用户界面&#xff08;GUI&#xff09;的Java库&#xff0c;它提供了丰富的组件和工具&#xff0c;用于创建交互式的桌面应用程序。Swing 是 Java Foundation Classes&#xff08;JFC&#xff09;的一部分&#xff0c;它是 Java 平台的一种标准用户…

超全面!Linux学习资料大合集,21套从入门到进阶,看这篇就够了

本文将为那些渴望学习Linux&#xff0c;但又缺乏相应资料和方向的朋友&#xff0c;提供21套Linux优质资料&#xff0c;包含入门到进阶&#xff0c;希望能对大家有所帮助。 此合集内容及其丰富&#xff0c;涉及方面颇多&#xff0c;不仅适合Linux入门学习的朋友&#xff0c;运维…

麻省理工最新开发AI模型,让机器人实现自主规划路线

文 | BFT机器人 麻省理工学院的研究人员独具匠心地应用了人工智能来解决仓库中的机器人路径规划问题&#xff0c;以此缓解交通拥堵的难题。据该学院介绍&#xff0c;他们的团队开发了一种深度学习模型&#xff0c;其效率比传统的强随机搜索方法高出近四倍&#xff0c;极大地提…

彻底剖析激光-视觉-IMU-GPS融合SLAM算法:理论推导、代码讲解和实战

自主导航是机器人与自动驾驶的核心功能&#xff0c;而SLAM技术是实现自主导航的前提与关键。现有的机器人与自动驾驶车辆往往会安装激光雷达&#xff0c;相机&#xff0c;IMU&#xff0c;GPS等多种模态的传感器&#xff0c;而且已有许多优秀的激光SLAM与视觉SLAM算法。但是每种…

nginx主动检测后端健康模块

一、前言 nginx也有自带的后端检测模块ngx_http_upstream_module&#xff0c;该模块可以做到基本的健康检查&#xff0c;因为该健康检查是被动的&#xff0c;当nginx有请求后&#xff0c;才会对后端服务进行健康检测&#xff0c;当检测到有故障时会将这个请求转发到正常的后端服…

云计算市场,从追求“规模制胜”到走向“用户分化”

文|智能相对论 作者|叶远风 通常来说&#xff0c;价格战放到任何行业&#xff0c;都不是什么好事。 如今&#xff0c;作为曾经的前沿技术创新&#xff0c;云计算行业正在被迫走入价格战的阴霾当中&#xff0c;引发业界担忧。 ECS&#xff08;云服务器&#xff09;最高降36%…

数据库之间数据迁移工具datax

简介 DataX 是阿里云 DataWorks数据集成 的开源版本&#xff0c;在阿里巴巴集团内被广泛使用的离线数据同步工具/平台。DataX 实现了包括 MySQL、Oracle、OceanBase、SqlServer、Postgre、HDFS、Hive、ADS、HBase、TableStore(OTS)、MaxCompute(ODPS)、Hologres、DRDS, databe…

2024.3.1 网络编程

1.思维导图 2.TCP机械臂测试 程序代码&#xff1a; #include <myhead.h> #define SER_IP "192.168.125.254" //服务器端IP #define SER_PORT 8888 //服务器端端口号#define CLI_IP "192.168.199.131" //客户端IP …

C++_数据类型_字符串型

作用 用于表示一串字符 两种风格 C风格字符串&#xff1a;char 变量名[] "字符串值” 示例 注意 C风格的字符串要用双括号括起来 C风格字符串&#xff1a;string 变量名 "字符串值” 注意 用C风格字符串的时候&#xff0c;要包含这个头文件#include <st…