深入理解Tomcat之二:自己动手实现一个简单的Tomcat
学习一个新的知识的过程就是 看别人的文章、听别人讲、自己查资料、自己给别人讲。我们对于新知识的认识成都以及理解深度都是在整个过程中不断的加深的。所以我一直提倡大家要乐于分享,当你给别人用组织系统化的语言或者文章将你头脑中的知识输出来,你就会发现,你对之前知识的理解又加深了一个程度。 而这一篇手写tomcat,其实也是我在学习tomcat架构的过程中,模仿别人的代码自己再手敲一遍,最后将思路和实现过程整理成文,输出给大家。
主要需求
上面就是mini tomcat的类图
各个类
MyRequest
自己封装的请求类,相当于servlet中的HttpRequest。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
public class MyRequest { private String url; private String method;
public MyRequest(InputStream inputStream) throws IOException { String httpRequest = ""; byte[] requestBytes = new byte[1024]; int length = inputStream.read(requestBytes);
if(length>0){ httpRequest = new String(requestBytes, 0, length); } String httpHead = httpRequest.split("\n")[0]; url = httpHead.split("\\s")[1]; method = httpHead.split("\\s")[0];
System.out.println("接收到请求-------》"); System.out.println("请求信息 :" + toString()); }
@Override public String toString() { return "url: " + url + ", method: " + method; }
public String getUrl() { return url; }
public String getMethod() { return method; }
}
|
MyResponse
- 要点在于手动按照http协议的格式进行响应,这样浏览器才可以识别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
public class MyResponse { private OutputStream outputStream;
public MyResponse(OutputStream outputStream) { this.outputStream = outputStream; }
public void write(String content) throws IOException { if(outputStream!=null) { StringBuffer httpResponse = new StringBuffer();
httpResponse.append("HTTP/1.1 200 OK\n") .append("Content-Type: text/html\n") .append("\r\n") .append("<html><body>") .append(content) .append("</body></html>");
System.out.println("返回信息:"+httpResponse.toString());
outputStream.write(httpResponse.toString().getBytes()); outputStream.close(); } } }
|
MyServlet
抽象的Servlet,可以继承它来有很多不同的实现。
- service 方法根据请求的方法分发到get或者post进行处理,这里与
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
public abstract class MyServlet { public abstract void doGet(MyRequest httpRequest, MyResponse httpResponse);
public abstract void doPost(MyRequest httpRequest, MyResponse httpResponse);
public void service(MyRequest request, MyResponse response) { if(request!=null) { if ("POST".equalsIgnoreCase(request.getMethod())) { doPost(request, response); } else if ("GET".equalsIgnoreCase(request.getMethod())) { doGet(request, response); } } } }
|
HelloServlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
public class HelloServlet extends MyServlet { @Override public void doGet(MyRequest httpRequest, MyResponse httpResponse) { try { httpResponse.write("get method in hello servlet !!"); } catch (IOException e) { e.printStackTrace(); } }
@Override public void doPost(MyRequest httpRequest, MyResponse httpResponse) { try { httpResponse.write("post method in hello servlet"); } catch (IOException e) { e.printStackTrace(); } } }
|
ServletMapping
其实是一个Bean,简单封装了配置信息,方便我们读取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| 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; } }
|
ServletMappingConfig
存着一个列表来保存配置,真正tomcat也不是这样实现的。我们只是为了效果方便实现。
1 2 3 4 5 6 7 8 9 10 11
|
public class ServletMappingConfig { public static List<ServletMapping> config = new ArrayList<ServletMapping>();
static { config.add(new ServletMapping("hello", "/hello", "com.practice.HelloServlet")); } }
|
MyTomcat
minitomcat的核心类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| public class MyTomcat { private int port = 8080; private HashMap<String, String> mapping = new HashMap<String, String>(); public MyTomcat(int port) { this.port = port; }
void start() { initservletMappings();
ServerSocket socket = null;
try { socket = new ServerSocket(port); System.out.println("Tomcat 启动成功~~");
while (true) { Socket accept = socket.accept(); InputStream inputStream = accept.getInputStream(); OutputStream outputStream = accept.getOutputStream();
MyRequest myRequest = new MyRequest(inputStream); MyResponse myResponse = new MyResponse(outputStream);
dispatch(myRequest, myResponse);
} } catch (IOException e) { e.printStackTrace(); } finally { if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
}
private void initservletMappings() { for (ServletMapping servletMapping : ServletMappingConfig.config) { mapping.put(servletMapping.getUrl(), servletMapping.getClazz()); } }
private void dispatch(MyRequest request, MyResponse response) { String url = request.getUrl(); String clazz = mapping.get(url);
if(clazz==null || clazz==""){ System.out.println("没有找到请求对应的链接:" + url); System.out.println(); return; }
try { Class<MyServlet> aClass = (Class<MyServlet>) Class.forName(clazz); MyServlet myServlet = aClass.newInstance(); myServlet.service(request, response); } catch (ClassNotFoundException e) { e.printStackTrace(); System.out.println("不存在该类"); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); }
}
public static void main(String[] args) { new MyTomcat(8081).start(); } }
|
最后我们运行main方法,在浏览器中访问localhost:8081/hello 就可以看到效果啦!
总结
这个tomcat很mini ,只简单实现了基本的功能,大家可以在这个基础上不断添加其他的功能,让这个minitomcat越来越接近真正的tomct!!
Last updated:
这里可以写作者留言,标签和 hexo 中所有变量及辅助函数等均可调用,示例:
http://yoursite.com/posts/6083/