缘起用了那么久tomcat,突然觉得自己对它或许并没有想象中的那么熟悉,所以趁着放假我研究了一下这只小猫咪,实现了自己的小tomcat,写出这篇文章同大家一起分享!照例附上github链接。项目结构项目结构如下:实现细节创建MyRequest对象首先创建自定义的请求类,其中定义url与method两个属性,表示请求的url以及请求的方式。其构造函数需要传入一个输入流,该输入流通过客户端的套接字对象得到。输入流中的内容为浏览器传入的http请求头,格式如下:GET /student HTTP/1.1Host: localhost:8080Connection: keep-alivePragma: no-cacheCache-Control: no-cacheUpgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9Cookie: Hm_lvt_eaa22075ffedfde4dc734cdbc709273d=1549006558; _ga=GA1.1.777543965.1549006558通过对以上内容的分割与截取,我们可以得到该请求的url以及请求的方式。package tomcat.dome;import java.io.IOException;import java.io.InputStream;//实现自己的请求类public class MyRequest { //请求的url private String url; //请求的方法类型 private String method; //构造函数 传入一个输入流 public MyRequest(InputStream inputStream) throws IOException { //用于存放http请求内容的容器 StringBuilder httpRequest=new StringBuilder(); //用于从输入流中读取数据的字节数组 byte[]httpRequestByte=new byte[1024]; int length=0; //将输入流中的内容读到字节数组中,并且对长度进行判断 if((length=inputStream.read(httpRequestByte))>0) { //证明输入流中有内容,则将字节数组添加到容器中 httpRequest.append(new String(httpRequestByte,0,length)); } //将容器中的内容打印出来 System.out.println(“httpRequest = [ “+httpRequest+” ]”); //从httpRequest中获取url,method存储到myRequest中 String httpHead=httpRequest.toString().split("\n")[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; } }创建MyResponse对象创建自定义的响应类,构造函数需要传入由客户端的套接字获取的输出流。定义其write方法,像浏览器写出一个响应头,并且包含我们要写出的内容content。package tomcat.dome;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{ //用来存放要写出数据的容器 StringBuffer stringBuffer=new StringBuffer(); stringBuffer.append(“HTTP/1.1 200 OK\r\n”) .append(“Content-type:text/html\r\n”) .append("\r\n") .append("<html><head><title>Hello World</title></head><body>") .append(content) .append("</body><html>"); //转换成字节数组 并进行写出 outputStream.write(stringBuffer.toString().getBytes()); //System.out.println(“sss”); outputStream.close(); } }创建MyServlet对象由于我们自己写一个Servlet的时候需要继承HttpServlet,因此在这里首先定义了一个抽象类——MyServlet对象。在其中定义了两个需要子类实现的抽象方法doGet和doSet。并且创建了一个service方法,通过对传入的request对象的请求方式进行判断,确定调用的是doGet方法或是doPost方法。package tomcat.dome;//写一个抽象类作为servlet的父类public abstract class MyServlet { //需要子类实现的抽象方法 protected abstract void doGet(MyRequest request,MyResponse response); protected abstract void doPost(MyRequest request,MyResponse response); //父类自己的方法 //父类的service方法对传入的request以及response //的方法类型进行判断,由此调用doGet或doPost方法 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这里我创建了两个业务相关类:StudentServlet和TeacherServlet。package tomcat.dome;import java.io.IOException;//实现自己业务相关的Servletpublic class StudentServlet extends MyServlet{ @Override protected void doGet(MyRequest request, MyResponse response) { //利用response中的输出流 写出内容 try { //System.out.println("!!!!!!!!!!!!!!!!!!"); response.write(“I am a student.”); //System.out.println(“9999999999999999”); }catch(IOException e) { e.printStackTrace(); } } @Override protected void doPost(MyRequest request, MyResponse response) { //利用response中的输出流 写出内容 try { response.write(“I am a student.”); }catch(IOException e) { e.printStackTrace(); } }}package tomcat.dome;import java.io.IOException;//实现自己业务相关的Servletpublic class TeacherServlet extends MyServlet{ @Override protected void doGet(MyRequest request, MyResponse response) { //利用response中的输出流 写出内容 try { response.write(“I am a teacher.”); }catch(IOException e) { e.printStackTrace(); } } @Override protected void doPost(MyRequest request, MyResponse response) { //利用response中的输出流 写出内容 try { response.write(“I am a teacher.”); }catch(IOException e) { e.printStackTrace(); } }}创建映射关系结构ServletMapping该结构实现的是请求的url与具体的Servlet之间的关系映射。package tomcat.dome;//请求url与项目中的servlet的映射关系public class ServletMapping { //servlet的名字 private String servletName; //请求的url private String url; //servlet类 private String 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; } public ServletMapping(String servletName, String url, String clazz) { super(); this.servletName = servletName; this.url = url; this.clazz = clazz; }}映射关系配置对象ServletMappingConfig配置类中定义了一个列表,里面存储着项目中的映射关系。package tomcat.dome;import java.util.ArrayList;import java.util.List;//创建一个存储有请求路径与servlet的对应关系的 映射关系配置类public class ServletMappingConfig { //使用一个list类型 里面存储的是映射关系类Mapping public static List<ServletMapping>servletMappings=new ArrayList<>(16); //向其中添加映射关系 static { servletMappings.add(new ServletMapping(“student”,"/student", “tomcat.dome.StudentServlet”)); servletMappings.add(new ServletMapping(“teacher”,"/teacher", “tomcat.dome.TeacherServlet”)); }}主角登场 MyTomcat!在服务端MyTomcat中主要做了如下几件事情:1)初始化请求的映射关系。2)创建服务端套接字,并绑定某个端口。3)进入循环,用户接受客户端的链接。4)通过客户端套接字创建request与response对象。5)根据request对象的请求方式调用相应的方法。6)启动MyTomcat!package tomcat.dome;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;//Tomcat服务器类 编写对请求做分发处理的相关逻辑public class MyTomcat { //端口号 private int port=8080; //用于存放请求路径与对应的servlet类的请求映射关系的map //相应的信息从配置类中获取 private Map<String, String>urlServletMap=new HashMap<>(16); //构造方法 public MyTomcat(int port) { this.port=port; } //tomcat服务器的启动方法 public void start() { //初始化请求映射关系 initServletMapping(); //服务端的套接字 ServerSocket serverSocket=null; try { //创建绑定到某个端口的服务端套接字 serverSocket=new ServerSocket(port); System.out.println(“MyTomcat 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); //根据请求对象的method分发请求 调用相应的方法 dispatch(request, response); //关闭客户端套接字 socket.close(); } } catch (IOException e) { e.printStackTrace(); } } //初始化请求映射关系,相关信息从配置类中获取 private void initServletMapping() { for(ServletMapping servletMapping:ServletMappingConfig.servletMappings) { urlServletMap.put(servletMapping.getUrl(), servletMapping.getClazz()); } } //通过当前的request以及response对象分发请求 private void dispatch(MyRequest request,MyResponse response) { //根据请求的url获取对应的servlet类的string String clazz=urlServletMap.get(request.getUrl()); //System.out.println("====="+clazz); try { //通过类的string将其转化为对象 Class servletClass=Class.forName(“tomcat.dome.StudentServlet”); //实例化一个对象 MyServlet myServlet=(MyServlet)servletClass.newInstance(); //调用父类方法,根据request的method对调用方法进行判断 //完成对myServlet中doGet与doPost方法的调用 myServlet.service(request, response); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } } //main方法 直接启动tomcat服务器 public static void main(String[] args) { new MyTomcat(8080).start(); } }测试结果