手动实现一个简易版 tomcat(yet)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手动实现一个简易版 tomcat(yet)相关的知识,希望对你有一定的参考价值。

https://mp.weixin.qq.com/s?__biz=MzA5MzcxNjY4Ng==&mid=2648107477&idx=1&sn=237afdcd8dc67f3a36aac8a38fcaada9&chksm=887be074bf0c69627659d3f076420ba7d477bc08405437eafe4d99ed22e6d2f9a53c80a73d87&mpshare=1&scene=1&srcid=0606qoAMsGtii5eGzil3h35x&key=abc5240e014eede13ee3b36db10a8651176fd1ff480250a3b2660676db6e3a4f4c0857207a64f65dbafc7c5276b2d8e8375d9a65c25572ed6df33fa45995f85c0fc5588801f2def497593ae43d6347d5&ascene=0&uin=MTA2NzUxMDAyNQ%3D%3D&devicetype=iMac+MacBookAir6%2C2+OSX+OSX+10.10.5+build(14F2511)&version=11020012&lang=zh_CN&pass_ticket=mxEo2xKCtH9iR2PLbxrAcJSXJ7pfbLGSU5PqNtNoXLtIgAf3or95FesHIZkUX27e

 

tomcat简介

 

Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。由于有了Sun 的参与和支持,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,Tomcat 5支持最新的Servlet 2.4 和JSP 2.0 规范。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。

 

技术分享图片

 

2手动实现

 

项目结构如下图所示

技术分享图片

 

接下来我们就开始一步步实现,有servlet开发经验的对于Request和Response一定不陌生,所以就先实现自己的Request和Response。首先实现Response如下所示。

 

package com.duomeng.tomcat;
import java.io.IOException;
import java.io.InputStream;
/**
*
*/
public class MyRequest {
   private String url;
   private String method;
   public MyRequest(InputStream inputStream) throws IOException {
       StringBuilder httpRequest = new StringBuilder();
       byte[] httpRequestByte = new byte[1014];
       int length = 0;
       if ((length = inputStream.read(httpRequestByte)) > 0) {
           httpRequest.append(new String(httpRequestByte,0,length));
       }
       System.out.println("httpRequest = [" + httpRequest + "]");
       /**
       GET /student HTTP/1.1
       Host: localhost:8080
       Connection: keep-alive
       Cache-Control: max-age=0
       Upgrade-Insecure-Requests: 1
       User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/66.0.3359.181 Safari/537.36
       Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,
        */
       String httpHead = httpRequest.toString().split(" ")[0];
       url = httpHead.split("\s")[1];
       method = httpHead.split("\s")[0];
       System.out.println("MyRequests = [" + this + "]");
   }
   public String getUrl() {
       return url;
   }
   public void setUrl(String url) {
       this.url = url;
   }
   public String getMethod() {
       return method;
   }
   public void setMethod(String method) {
       this.method = method;
   }
}

 

接着来实现自己的Reponse,如下图所示。

package com.duomeng.tomcat;
import java.io.IOException;
import java.io.OutputStream;
public class MyResponse {
   private OutputStream outputStream;
   public MyResponse(OutputStream outputStream) {
       this.outputStream = outputStream;
   }
   public  void write(String content) throws IOException {
       /**
        * HTTP/1.1 200 OK
        Content-type:text/html
        */
       StringBuffer stringBuffer = new StringBuffer();
       stringBuffer.append("HTTP/1.1 200 OK ")
               .append("Content-type:text/html ")
               .append(" ")
               .append("<html><head><title>Hello World</title></head><body>")
               .append(content)
               .append("</body><html>");
       outputStream.write(stringBuffer .toString().getBytes());
       outputStream.close();
   }
}

 

有了自己的请求信息和响应信息,那接下来就要写servlet了。通常写自己的Servlet的时候要继承一个Servlet的父类HttpServlet,所以首先要写一个Servlet的父类,代码如下。

package com.duomeng.tomcat;
public abstract class MyServlet {
   protected abstract void doGet(MyRequest request,MyResponse response);
   protected abstract void doPost(MyRequest request,MyResponse response);
   public void service(MyRequest request,MyResponse response) throws NoSuchMethodException {
       if (request.getMethod().equalsIgnoreCase("POST")) {
           doPost(request,response);
       }else if(request.getMethod().equalsIgnoreCase("GET")){
           doGet(request,response);
       }else {
           throw new NoSuchMethodException("not support");
       }
   }
}

 

那么接下来就要写自己业务相关的Servlet,这里写一个和学生相关的Servlet,叫StudentServlet,还有一个和老师相关的Servlet,叫TeacherServlet。具体的代码分别如下。

package com.duomeng.tomcat;
import java.io.IOException;
public class StudentServlet extends MyServlet{
   @Override
   protected void doGet(MyRequest request, MyResponse response) {
       try {
           response.write("I am a student.");
       } catch (IOException e) {
           e.printStackTrace();
       }
   }
   @Override
   protected void doPost(MyRequest request, MyResponse response) {
       try {
           response.write("I am a student.");
       } catch (IOException e) {
           e.printStackTrace();
       }
   }
}

 

和老师相关的Servlet如下:

package com.duomeng.tomcat;
import java.io.IOException;
public class TeacherServlet extends MyServlet{
   @Override
   protected void doGet(MyRequest request, MyResponse response) {
       try {
           response.write("I am a treacher.");
       } catch (IOException e) {
           e.printStackTrace();
       }
   }
   @Override
   protected void doPost(MyRequest request, MyResponse response) {
       try {
           response.write("I am a treacher.");
       } catch (IOException e) {
           e.printStackTrace();
       }
   }
}

 

因为浏览器在请求的时候还会请求浏览器上面显示的favicon.ico,所以要编写一个相关的servlet来处理请求,代码如下。

 

package com.duomeng.tomcat;
import java.io.IOException;
public class FaviconServlet extends MyServlet{
   @Override
   protected void doGet(MyRequest request, MyResponse response) {
       try {
           response.write("Favicon");
       } catch (IOException e) {
           e.printStackTrace();
       }
   }
   @Override
   protected void doPost(MyRequest request, MyResponse response) {
       try {
           response.write("Favicon");
       } catch (IOException e) {
           e.printStackTrace();
       }
   }
}

 

以上就是写的跟具体实际当中业务相关的代码,还记得在Servlet的时候要在项目web.xml中配置请求地址和具体的servlet的映射关系,所以此处要手动来实现一个映射的关系。具体代码如下。

 

package com.duomeng.tomcat;
public class ServletMapping {
   private String servletName;
   private String url;
   private String clazz;
   public ServletMapping(String servletName, String url, String clazz) {
       this.servletName = servletName;
       this.url = url;
       this.clazz = clazz;
   }
   public String getServletName() {
       return servletName;
   }
   public void setServletName(String servletName) {
       this.servletName = servletName;
   }
   public String getUrl() {
       return url;
   }
   public void setUrl(String url) {
       this.url = url;
   }
   public String getClazz() {
       return clazz;
   }
   public void setClazz(String clazz) {
       this.clazz = clazz;
   }
}

 

以上是每个具体的Servlet的信息,接下来要写一个存储有映射关系的类,代码如下。

package com.duomeng.tomcat;
import java.util.ArrayList;
import java.util.List;
public class ServletMappingConfig {
   public static List<ServletMapping> servletMappings = new ArrayList<ServletMapping>(16);
   static {
       servletMappings.add(new ServletMapping("student","/student","com.duomeng.tomcat.StudentServlet"));
       servletMappings.add(new ServletMapping("teacher","/teacher","com.duomeng.tomcat.TeacherServlet"));
       servletMappings.add(new ServletMapping("favicon","/favicon.ico","com.duomeng.tomcat.FaviconServlet"));
   }
}

 

接下来就是真正的主角登场了,那就是对于请求做分发处理的相关逻辑了,具体代码如下所示。

 

package com.duomeng.tomcat;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class MyTomcat {
   private int port = 8080;
   private Map<String,String> urlServletMap = new HashMap<String, String>(16);
   public MyTomcat(int port) {
       this.port = port;
   }
   public void start(){
       //初始化请求映射关系
       initServletMapping();
       ServerSocket serverSocket = null;
       try {
           serverSocket = new ServerSocket(port);
           System.out.println("My tomcat begin start");
           while (true){
               Socket socket = serverSocket.accept();
               InputStream inputStream = socket.getInputStream();
               OutputStream outputStream = socket.getOutputStream();
               MyRequest request = new MyRequest(inputStream);
               MyResponse response = new MyResponse(outputStream);
               //分发请求
               dispatch(request,response);
               socket.close();
           }
       } catch (IOException e) {
           e.printStackTrace();
       }
   }
   private void initServletMapping(){
       for (ServletMapping servletMapping:ServletMappingConfig.servletMappings) {
           urlServletMap.put(servletMapping.getUrl(),servletMapping.getClazz());
       }
   }
   private void dispatch(MyRequest request,MyResponse response){
       String clazz = urlServletMap.get(request.getUrl());
       try {
           Class servletClass = Class.forName(clazz);
           MyServlet myServlet = (MyServlet)servletClass.newInstance();
           myServlet.service(request,response);
       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       } catch (IllegalAccessException e) {
           e.printStackTrace();
       } catch (InstantiationException e) {
           e.printStackTrace();
       } catch (NoSuchMethodException e) {
           e.printStackTrace();
       }
   }
   public static void main(String[] args) {
       new MyTomcat(8080).start();
   }
}

 

如果没有问题的话,启动main函数之后,在浏览器发起具体的请求会看到如下显示内容。

技术分享图片

技术分享图片



















































































































































































































































以上是关于手动实现一个简易版 tomcat(yet)的主要内容,如果未能解决你的问题,请参考以下文章

如何手写一个简易版SpringMVC

简易版Tomcat搞起

Kotlin学习之旅解决错误:kotlin.NotImplementedError: An operation is not implemented: Not yet implemented(代码片段

手写一个简易版Tomcat

react---手动封装一个简易版的redux---韶华

Go 手动打造一个简易版的 try cache