如何仿写简易tomcat 实现思路+代码详细讲解

仿写之前,我们要搞清楚都要用到哪些技术

  1. 自定义注解,比如Tomcat使用的是@Servlet,我们可以定义一个自己的@MyServlet
  2. 构造请求体和返回体,比如tomcat使用HttpRequest,我们可以自己定义myHttpRequest
  3. java去遍历一个指定目录,然后获取到.java文件,再获取到带有@MyServlet注解的类
  4. 然后将这个注解里的path和这个类本身映射成map
  5. 通过反射去调用该类的方法(doGet、doPost)
  6. 还需要用到socket来监听消息,并且对监听到的消息进行处理

第一步:自定义注解

@Target(ElementType.TYPE)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface MyServlet {String path() default "";
}

第二步:定义HttpRequest以及HttpResponse、

public class MyHttpRequest {//定义一个map,用来存放请求体中的参数,key是参数名称,value是参数值public Map<String,String> map = new HashMap<>();public String getParameter(String key){return map.get(key);}
}
public class MyHttpResponse {public OutputStream outputStream;public static final String responsebody = "HTTP/1.1 200+\r\n" + "Content-Type:text/html+\r\n"+ "\r\n";public MyHttpResponse(OutputStream outputStream) {this.outputStream = outputStream;}
}

第三步:遍历整个目录,把Java文件放入list中

    private static void func(File file){File[] files = file.listFiles();String s;for (File file1 : files) {if (file1.isDirectory()){func(file1);}if (file1.isFile()){//取src之后的名字s = file1.toString().split("src")[1];//去掉src后边的第一个\,得到全类名s = s.substring(1);//判断是不是以.java结尾的文件if (s.length() >=5 && s.substring(s.length() - 5).equals(".java")){//把全类名中的\替换成.s = s.replace('\\','.');//去掉后缀名.javas = s.substring(0,s.length()-5);//把类名加入到list中javaclasses.add(s);}}}}

第四步:找出带有Servlet注解的Java文件,并把注解中的path,类对象放入到map中

    public static void getServlet() throws ClassNotFoundException {for (int i = 0; i < javaclasses.size(); i++) {String path = javaclasses.get(i);Class<?> cl = Class.forName(path);if (cl.isAnnotationPresent(MyServlet.class)){servletMap.put(cl.getAnnotation(MyServlet.class).path(),cl);}}}

第五步:创建socket连接

InetAddress localHost = InetAddress.getLocalHost();System.out.println("localhost" + localHost);ServerSocket serverSocket = new ServerSocket(8080, 10, localHost);System.out.println("等待建立连接");Socket server = serverSocket.accept();System.out.println("连接已建立");

第六步:定义线程接收报文

        HttpAcceptThread httpAcceptThread = new HttpAcceptThread(server);Thread accept = new Thread(httpAcceptThread);accept.start();accept.join();

HttpAcceptThread类内容如下:

class HttpAcceptThread implements Runnable{private Socket socket;ArrayList<String> strings = new ArrayList<>();public HttpAcceptThread(Socket socket) {this.socket = socket;}@Overridepublic void run() {System.out.println("开始接收http");try {BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));String s;while ((s = reader.readLine()).length() != 0){try {strings.add(s);System.out.println(s);} catch (Exception e){System.out.println("接收Http进程结束");break;}}System.out.println("接收http进程结束");} catch (IOException e) {e.printStackTrace();}}
}

第七步:处理httprequest,也就是通过反射去调用doGet和doPost方法

这一步有些复杂,尤其是对url切割时,但我给每一步都加了注释,方便理解

             GET /address1?a=111&b=222

   private static void requestHttp(Socket socket,String http) throws IOException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {//GET /address1?a=111&b=222(拿获取到的这个url举例)//先通过空格判断是GET还是POSTString requestStyle = http.split(" ")[0];if (requestStyle.equals("GET")){//如果是GET,取空格后面部分,即/address1?a=111&b=222String httpPathAndParameter = http.split(" ")[1];//定义httpPathString httpPath;//创建httpRequest对象MyHttpRequest myHttpRequest = new MyHttpRequest();//通过索引位置判断url里边有没有带?if (httpPathAndParameter.indexOf("?") != -1){//如果有,由于有个/,因此我们要先拿到address1?a=111&b=222这部分httpPath = httpPathAndParameter.substring(1);//获取问号前面部分,即address1,\\作为转义字符使用httpPath = httpPath.split("\\?")[0];System.out.println(httpPath);//获取问号后面部分的所有参数String parameterString = httpPathAndParameter.split("\\?")[1];//使用&分开String[] parameters = parameterString.split("&");for (int i = 0; i < parameters.length; i++) {//把参数及其值仿佛request的map中myHttpRequest.map.put(parameters[i].split("=")[0],parameters[i].split("=")[1]);}} else {//如果不存在?,也就说明不存在参数,我们只需要获取httpPathhttpPath = httpPathAndParameter.substring(1);System.out.println(httpPath);}//创建HttpResponse对象OutputStream outputStream = socket.getOutputStream();MyHttpResponse myHttpResponse = new MyHttpResponse(outputStream);//反射调用doGetClass servletClass = servletMap.get(httpPath);Method doGet = servletClass.getMethod("doGet", MyHttpRequest.class, MyHttpResponse.class);doGet.invoke(servletClass.newInstance(),myHttpRequest,myHttpResponse);} else {//如果不是Get请求,也按照同样的步骤,先取出/address1String httpPath = http.split(" ")[1];//去掉/,只留下address1httpPath = httpPath.substring(1);System.out.println(httpPath);MyHttpRequest myHttpRequest = new MyHttpRequest();OutputStream outputStream = socket.getOutputStream();MyHttpResponse myHttpResponse = new MyHttpResponse(outputStream);//根据httpPath取出类信息Class servletClass = servletMap.get(httpPath);//获取doPost方法Method doPost = servletClass.getMethod("doPost", MyHttpRequest.class, MyHttpResponse.class);//调用doPost方法doPost.invoke(servletClass.newInstance(),myHttpRequest,myHttpResponse);}}

最后一步:把上面这些方法整合起来,在主方法中调用,同时定义好全局变量

public class MyTomcat {//用于存放Java类的全类名public static ArrayList<String> javaclasses = new ArrayList<>();//用于存放Servlet的类对象,其中key是Servlet的url,value是servlet的类对象public static HashMap<String,Class> servletMap = new HashMap<>();public static void main(String[] args) throws ClassNotFoundException, IOException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {String inputPath = "D:\\JavaProject\\practice\\src\\tomcat";File file = new File(inputPath);//获取.java后缀文件,并获取全类名func(file);System.out.println(javaclasses);//获取带有servlet注解的类对象,并放到map中。getServlet();System.out.println(servletMap);InetAddress localHost = InetAddress.getLocalHost();System.out.println("localhost" + localHost);ServerSocket serverSocket = new ServerSocket(8080, 10, localHost);System.out.println("等待建立连接");Socket server = serverSocket.accept();System.out.println("连接已建立");//定义线程接收http报文HttpAcceptThread httpAcceptThread = new HttpAcceptThread(server);Thread accept = new Thread(httpAcceptThread);accept.start();accept.join();//处理请求requestHttp(server,httpAcceptThread.strings.get(0));}

然后就可以进行测试了,在测试类上方加上我们已经定义好的@MyServlet注解

@MyServlet(path = "address1")
public class Servlet1 {public void doGet(MyHttpRequest request, MyHttpResponse response) throws IOException {System.out.println("address1 GET响应:");System.out.println("a=" + request.getParameter("a"));System.out.println("\n响应的http如下:");String resp = MyHttpResponse.responsebody + "<!DOCTYPE html>\n" +"<html>\n" +"<head>\n" +"    <meta charset=\"utf-8\" />\n" +"</head>\n" +"<body>\n" +" \n" +"    <form name=\"my_form\" method=\"POST\">\n" +"        <input type=\"button\" value=\"按下\" onclick=\"alert('你按下了按钮')\">\n" +"    </form>\n" +" \n" +"</body>\n" +"</html>";System.out.println(resp);response.outputStream.write(resp.getBytes());response.outputStream.flush();response.outputStream.close();}public void doPost(MyHttpRequest request, MyHttpResponse response) throws IOException {System.out.println("\n响应的http如下:");String resp = MyHttpResponse.responsebody +"{\"sorry\":\"we only respond to method GET now\"},\r\n" +"";System.out.println(resp);response.outputStream.write(resp.getBytes());response.outputStream.flush();response.outputStream.close();}
}

然后启动项目

 可以看到本机ip地址,然后通过浏览器地址栏访问

 这样就实现了一个简单的tomcat

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

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

相关文章

数据结构——链表详解

链表 文章目录 链表前言认识链表单链表结构图带头单循环链表结构图双向循环链表结构图带头双向循环链表结构图 链表特点 链表实现(带头双向循环链表实现)链表结构体(1) 新建头节点(2) 建立新节点(3)尾部插入节点(4)删除节点(5)头部插入节点(6) 头删节点(7) 寻找节点(8) pos位置…

APP外包开发原生和H5的区别

原生开发和H5开发是两种不同的方法&#xff0c;用于创建移动应用程序。它们具有各自的特点、优势和劣势&#xff0c;适用于不同的应用场景。以下是原生开发和H5开发之间的一些主要区别&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发…

未来公文的智能化进程

随着技术的飞速发展&#xff0c;公文——这个有着悠久历史的官方沟通方式&#xff0c;也正逐步走向智能化的未来。自动化、人工智能、区块链...这些现代科技正重塑我们的公文制度&#xff0c;让其变得更加高效、安全和智慧。 1.语义理解与自动生成 通过深度学习和NLP&#xff…

从零基础到精通IT:探索高效学习路径与成功案例

文章目录 导语&#xff1a;第一步&#xff1a;明确学习目标与方向选择适合的IT方向设定具体的学习目标咨询和调研 第二步&#xff1a;系统学习基础知识选择适合的编程语言学习数据结构和算法掌握操作系统和计算机网络基础 第三步&#xff1a;实践项目锻炼技能选择合适的项目编写…

Redis——哨兵模式(docker部署redis哨兵)+缓存穿透和雪崩

哨兵模式 自动选取主机的模式。 概述 主从切换技术的方法是:当主服务器宕机后&#xff0c;需要手动把一台从服务器切换为主服务器&#xff0c;这就需要人工干预&#xff0c;费事费力&#xff0c;还会造成段时间内服务不可用。这不是一种推荐的方式&#xff0c;更多时候&…

前端开发怎么解决性能优化的问题? - 易智编译EaseEditing

前端性能优化是确保网站或应用在加载速度、响应性和用户体验等方面达到最佳状态的关键任务。以下是一些解决前端性能优化问题的方法&#xff1a; 压缩和合并代码&#xff1a; 压缩和合并CSS、JavaScript和HTML文件可以减少文件大小&#xff0c;加快加载速度。使用压缩工具&am…

vue vs react

vue 简介&#xff1a;渐进式 JavaScript 框架 来源&#xff1a;最初由 Evan You &#xff08;尤雨溪&#xff09;于2014年开发。Evan You之前在Google研究过AngularJS&#xff0c;并提取了Angular的部分特性以提供一个更轻量级的框架 版本&#xff1a; vue 1x&#xff1a;2014…

协同过滤推荐算法-基于Django+mysql的智能水果销售系统设计(可做计算机毕设)

随着科技的不断发展&#xff0c;智能化已经成为各行各业的趋势&#xff0c;水果销售行业也不例外。智能水果销售系统就是应运而生的一种智能化解决方案&#xff0c;它可以为用户提供更加便捷、高效的购物体验。其中&#xff0c;系统模块是智能水果销售系统的重要组成部分。 系…

【Apollo学习笔记】—— Planning模块

前言 本文记录学习planning模块时的一些笔记&#xff0c;总体流程参照https://zhuanlan.zhihu.com/p/61982682中的流程图&#xff0c;如上图所示。 planning_component modules/planning/planning_component.cc PlanningComponent::Init部分首先完成规划模式的选择&#xff…

【Linux】POSIX信号量和基于环形队列的生产消费者模型

目录 写在前面的话 什么是POSIX信号量 POSIX信号量的使用 基于环形队列的生产消费者模型 写在前面的话 本文章主要先介绍POSIX信号量&#xff0c;以及一些接口的使用&#xff0c;然后再编码设计一个基于环形队列的生产消费者模型来使用这些接口。 讲解POSIX信号量时&#x…

windows结束explorer进程后桌面白屏解决

背景 结束进程时一不小心一起删掉explorer.exe &#xff0c;这个文件结束桌面就一片白 &#xff0c; 解决&#xff1a; 不要关机&#xff0c;同时按键盘上ctrlshiftesc ,重新进入任务管理器&#xff0c;接着点“进程”选项&#xff0c;按左上角文件选项&#xff0c;进入下拉菜单…

备份或同步数据?跨国大文件传输的不同需求与解决方案

信息化时代的到来&#xff0c;使得许多个人、组织、企业在日常生活中都需要对数据进行备份或同步。但不同的应用场景和需求&#xff0c;也需要不同的备份和同步方式。而在跨国大文件传输方面&#xff0c;更是需要根据不同需求选择合适的传输方案。下面将分别介绍备份与同步数据…

BeanFactoryApplicationContext之间的关系

1**.BeanFactory与ApplicationContext之间的关系** &#xff08;1&#xff09;从继承关系上来看&#xff1a; ​ BeanFactory它是ApplicationContext 的父接口 &#xff08;2&#xff09;从功能上来看&#xff1a; ​ BeanFactory才是spring中的核心容器&#xff0c;而Appli…

【数学建模】-- 数学规划模型

概述&#xff1a; 什么是数学规划&#xff1f; 数学建模中的数学规划是指利用数学方法和技巧对问题进行数学建模&#xff0c;并通过数学规划模型求解最优解的过程。数学规划是一种数学优化方法&#xff0c;旨在找到使目标函数达到最大值或最小值的变量取值&#xff0c;同时满足…

VGG简单学习

VGG简单学习 简单介绍 在AlexNet网络的基础上&#xff0c;为了设计深层神经网络&#xff0c;牛津大学设计了VGG网络,采用块的设计理念&#xff0c;将AlexNet中多个重复的卷积层和池化层组成一个块 论文中&#xff0c;使用3x3卷积核&#xff0c;padding1的卷积层 和带有2x2的汇…

WebMagic - 创意前端项目集合(点击链接可在电脑上查看效果)

WebMagic - 创意前端项目集合 欢迎来到 WebMagic 仓库&#xff01;这里汇集了一系列令人惊叹的前端项目&#xff0c;涵盖了HTML5、CSS3和JS等多项技术。无论你是前端开发者、设计师&#xff0c;还是对创意互动内容感兴趣的人&#xff0c;这个仓库都将为你带来无尽的惊喜。 每…

LangChain手记 Chains

整理并翻译自DeepLearning.AILangChain的官方课程&#xff1a;Chains&#xff08;源代码可见&#xff09; Chains 直译链&#xff0c;表达的意思更像是对话链&#xff0c;对话链的背后是思维链 LLM Chain&#xff08;LLM链&#xff09; 首先介绍了一个最简单的例子&#xff0c…

代码审计-java项目-组件漏洞审计

代码审计必备知识点&#xff1a; 1、代码审计开始前准备&#xff1a; 环境搭建使用&#xff0c;工具插件安装使用&#xff0c;掌握各种漏洞原理及利用,代码开发类知识点。 2、代码审计前信息收集&#xff1a; 审计目标的程序名&#xff0c;版本&#xff0c;当前环境(系统,中间件…

图数据库_Neo4j和SpringBoot整合使用_实战创建明星关系图谱---Neo4j图数据库工作笔记0010

然后我们再来看一下这个明星关系图谱 可以看到这里 这个是原来的startRelation 我们可以写CQL去查询对应的关系 可以看到,首先查询出来以后,然后就可以去创建 我们可以把写的创建明星关系的CQL,拿到 springboot中去执行 可以看到,这里我们先写一个StarRelationRepository,然…

Java二分法查找

二分法&#xff1a;首先需要一个由小到大排序好的数组&#xff0c;先找到其中间值&#xff0c;然后进行比较如果比较中间值大的话则向前找。如果比要找的小&#xff0c;则向后找。 代码实现&#xff1a; //定义查询方法 public static int searchTarget(int[] nums, int targ…