本文共 6167 字,大约阅读时间需要 20 分钟。
作为java开发者,都知道Tomcat是一个家户喻晓的Web服务器。手写一个Tomcat对我们理解Tomcat的工作原理,还有servlet的执行流程有很大的帮助,下面是我手写一个简要版Tomcat的总结和收获!
首先,先还原Tomcat的本质:
相信大家之前在开发的时候,虽然已经能上手开发web程序,但是对于Servlet的执行流程以及项目还会存在很多疑问,下面是我总结的我之前的疑问也给出相应的总结:1.为什么要配置一个web.xml ?
2.为什么要继承Servlet,不继承不行吗? 3.为什么要重写doPost/doGet方法? 4.HttpRequest和HttpResponse是哪里冒出来的,自己从来没new过? 5.在浏览器输入Url是如何转换为调用java方法? 下面是我总结的Tomcat的工作流程: 1.加载配置文件,初始化ServletMapping 2.等待用户请求 3.获取到Socket对象,将socket.getInputStream()封装成request 将socket.getOutputStream()封装成response 4.实现动态调用doGet/doPost方法,并且能够自定义返回结果详细流程:
首先用户发送url访问地址,也就是我们浏览器地址栏的地址,然后web.xml或者web.properties里面配置好访问路径和对应要交给处理的servlet的全类名,然后项目初始化的时候,加载web.xml配置文件,将其中的url和className提取出来,将其封装到ServletMapping中,这是一个键值对,键是url,值是className。然后就这就去找我们自己定义的servlet,我们自己写的servlet都会继承HttpServlet,然后就会执行父类的service在这个方法中,判断是执行doGet()还是doPost,而接收这些http协议的是HttpRequest,这个对象获取到用户的url和执行方法,然后再通过HttpResponse响应结果。下面是我的项目结构:
web.properties配置文件:
主程序
package com.ycc.tomcat;import java.io.FileInputStream;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;import java.util.Properties;import com.ycc.http.YccRequest;import com.ycc.http.YccResponse;import com.ycc.http.YccServlet;//主要思路://1.加载配置文件,初始化ServletMapping//2.等待用户请求,用一个死循环等待用户请求//3.获取到Socket对象,将socket.getInputStream()封装成request// 将socket.getOutputStream()封装成response//4.实现动态调用doGet/doPost方法,并且能够自定义返回结果public class YccTomcat { private int port=8072;//指定端口号 private ServerSocket server; private MapservletMapping=new HashMap (); private Properties webxml=new Properties(); //1.加载配置文件,初始化ServletMapping private void init() { //加载web.xml文件,同时初始化ServletMapping对象 try { String WEB_INF=this.getClass().getResource("/").getPath(); FileInputStream fis=new FileInputStream(WEB_INF+"web.properties"); webxml.load(fis); for (Object k : webxml.keySet()) { String key=k.toString(); if(key.endsWith(".url")) { //投机取巧,取到servletName String servletName=key.replaceAll("\\.url$",""); String url=webxml.getProperty(key); String className=webxml.getProperty(servletName+".class"); YccServlet obj=(YccServlet)Class.forName(className).newInstance(); servletMapping.put(url, obj); } } } catch (Exception e) { e.printStackTrace(); } } private void process(Socket client) throws Exception { //3.获取到Socket对象,将socket.getInputStream()封装成request // 将socket.getOutputStream()封装成response InputStream is=client.getInputStream(); OutputStream out=client.getOutputStream(); YccRequest request=new YccRequest(is); YccResponse response=new YccResponse(out); //4.实现动态调用doGet/doPost方法,并且能够自定义返回结果 //想办法拿到用户所请求的url String url=request.getUrl(); if(servletMapping.containsKey(url)) { servletMapping.get(url).service(request, response); }else { response.write("404 - Not Found"); } // OutputStream os=socket.getOutputStream();// os.write("yechengcaho Tomact".getBytes());// out.flush(); out.close(); //为什么要关闭client?因为HTTP请求都是采用短连接 client.close(); } //启动Tomcat public void start() { //1.加载配置文件,初始化ServletMapping init(); try { server=new ServerSocket(this.port); System.out.println("Ycc Tomcat 已启动,监听的端口是:"+this.port); //得到一个客户端 //Socket socket=server.accept(); //2.等待用户请求,用一个死循环等待用户请求 while(true) { Socket client=server.accept(); process(client); } //System.out.println(socket); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { new YccTomcat().start(); }}
YccServlet,也就是HttpServlet
package com.ycc.http;public abstract class YccServlet { public void service(YccRequest request,YccResponse response)throws Exception { //由service方法决定,来调用doGet或者是doPost if("GET".equalsIgnoreCase(request.getMethod())) { doGet(request, response); }else { doPost(request, response); } } public abstract void doGet(YccRequest request,YccResponse response)throws Exception; public abstract void doPost(YccRequest request,YccResponse response)throws Exception;}
YccRequest,也就是HttpRequest
package com.ycc.http;
import java.io.InputStream;
import java.util.Map;public class YccRequest {
private String method;private String url;public YccRequest(InputStream is) { try { //HTTP协议就是一串字符串 String content=""; byte[] buff=new byte[1024]; int len=0; if((len=is.read(buff))>0) { content=new String(buff,0,len); System.out.println(content); } String line=content.split("\\n")[0]; String[] arr=line.split("\\s"); this.method=arr[0]; this.url=arr[1].split("\\?")[0]; }catch(Exception e) { e.printStackTrace(); }}public String getMethod() { return method;}public String getUrl() { return url;}public MapgetParmenter(){ return null;}
}
YccResponse,也就是HttpResponse
package com.ycc.http;import java.io.OutputStream;public class YccResponse { private OutputStream os; public YccResponse(OutputStream os) { this.os=os; } public void write(String outString)throws Exception{ os.write(outString.getBytes()); }}
第一个测试Servlet:
package com.ycc.servlet;import com.ycc.http.YccRequest;import com.ycc.http.YccResponse;import com.ycc.http.YccServlet;public class FirstServlet extends YccServlet { @Override public void doGet(YccRequest request, YccResponse response) throws Exception { this.doPost(request, response); } @Override public void doPost(YccRequest request, YccResponse response) throws Exception { response.write("Welcome to yechengchao Tomcat!"); response.write("This is yechengchao01 Servlet"); }}
第二个测试Servlet:
package com.ycc.servlet;import com.ycc.http.YccRequest;import com.ycc.http.YccResponse;import com.ycc.http.YccServlet;public class SecondServlet extends YccServlet { @Override public void doGet(YccRequest request, YccResponse response) throws Exception { this.doPost(request, response); } @Override public void doPost(YccRequest request, YccResponse response) throws Exception { response.write("Welcome to yechengchao Tomcat!"); response.write("This is yechengchao02 Servlet"); }}
测试效果
转载地址:http://kiyen.baihongyu.com/