《How Tomcat Works》读书笔记
Posted 默默的看雨下
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《How Tomcat Works》读书笔记相关的知识,希望对你有一定的参考价值。
《How Tomcat Works》读书笔记(二)
这是《How Tomcat Works》第一二章的读书笔记。第一张主要写了一个静态资源处理的web服务器,第二章加了对servlet的处理。
1. 概述
1.1 架构
- HttpServer:表示Http服务器,与客户端通信,处理Http请求。
- StaticResourceProcessor:对静态资源请求进行处理。
- ServletProcessor:对Servlet资源请求进行处理。
- Request:表示Http请求,实现了ServletRequest接口。
- Response:表示Http响应,实现了ServletResponse接口。
- RequestFacade/ResponseFacade:Request/Response的门面类。
- PrimitiveServlet:表示一个Servlet类。
处理流程:
1.2 一些代码
HttpServer处理逻辑:
1 // check if this is a request for a servlet or 2 // a static resource 3 // a request for a servlet begins with "/servlet/" 4 if (request.getUri().startsWith("/servlet/")) { 5 ServletProcessor1 processor = new ServletProcessor1(); 6 processor.process(request, response); 7 } 8 else { 9 StaticResoureProcessor processor = new StaticResourceProcessor(); 10 processor.process(request, response); 11 }
ServletProcessor中通过类加载器把.class文件动态的加载为Servlet对象:
1 public void process(Request request, Response response) { 2 String uri = request.getUri(); 3 String servletName = uri.substring(uri.lastIndexOf("/") + 1); 4 URLClassLoader loader = null; 5 try { 6 // create a URLClassLoader 7 URL[] urls = new URL[1]; 8 URLStreamHandler streamHandler = null; 9 File classPath = new File(Constants.WEB_ROOT); 10 // the forming of repository is taken from the 11 // createClassLoader method in 12 // org.apache.catalina.startup.ClassLoaderFactory 13 String repository =(new URL("file", null, 14 classPath.getCanonicalPath() + File.separator)).toString() ; 15 // the code for forming the URL is taken from 16 // the addRepository method in 17 // org.apache.catalina.loader.StandardClassLoader. 18 urls[0] = new URL(null, repository, streamHandler); 19 loader = new URLClassLoader(urls); 20 } catch (IOException e) { 21 } 22 Class myClass = null; 23 try { 24 myClass = loader.loadClass(servletName); 25 } catch (ClassNotFoundException e) { 26 } 27 Servlet servlet = null; 28 try { 29 servlet = (Servlet) myClass.newInstance(); 30 // 使用门面设计模式 31 RequestFacade rquestFacade = new RequestFacade(request); 32 ResponseFacade responseFacade = new RespondeFacade(response); 33 // 交由Servlet进行处理 34 servlet.service(requestFacade, responseFacade); 35 } 36 }
1.3 设计模式
在这里使用了门面的设计模式,目的是安全性。在StaticResourceProcessor.processor()方法中会把Request、Response对象传给Servelt进行处理,在后面程序猿处理Servlet请求时就可以把ServletRequest向下转型为Request对象,然后使用不该在此使用的方法,如:parse()方法。所以,在这里使用了门面设计模式。
ResponseFacade类:
1 public class ResponseFacade implements ServletResponse { 2 private ServletResponse servletResponse = null; 3 4 // 通过构造方法传入真正的Response,然后向上转型为ServletResponse 5 public ResponseFacade(Response response) { 6 this.servletResponse = response; 7 } 8 /** 9 * 后面都是实现ServlerResponse接口要实现的方法,就不写完了。 10 */ 11 @Override 12 public String getCharacterEncoding() { 13 return servletResponse.getCharacterEncoding(); 14 } 15 @Override 16 public String getContentType() { 17 return servletResponse.getContentType(); 18 }
2. 注意
2.1 HTTP协议
HTTP协议是应用层协议,它基于TCP/IP协议进行传输数据。在客户端与web服务器解析数据时,必须有相关的头部数据。所以web服务器给客户端发出响应,该响应必须加入头部数据。自己在写该代码时,就忘了加头部数据,出个bug,解决了一段时间才发现。
PrimitiveServlet的service()方法:
1 public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { 2 String header = "HTTP/1.1 200 OK\\r\\n" 3 + "Content-Type: text/html;charset=UTF-8\\r\\n" 4 + "\\r\\n"; 5 String content = "<html>\\r\\n" 6 + "<head>" 7 + "</head>" 8 + "<body>" 9 + "Primitive Servlet" 10 + "</body>" 11 + "</html>"; 12 PrintWriter writer = res.getWriter(); 13 writer.write(header); 14 writer.write(content); 15 writer.flush(); 16 writer.close(); 17 }
我们平时使用Servlet开发时,直接继承HttpServlet,容器会自动的加上HTTP Response相关信息。
2.2 MIME类型
HTTP Respose header中有一个ContentType字段,它要求告诉客户端该Response正文的内容MIME类型,我们可以通过下面的API获得某一个文件的MIME类型。
String mimeType = URLConnection.getFileNameMap().getContentTypeFor(fileName)
2.2 .class文件
该系统使用到了URLClassLoader加载Class对象,ClassLoader把字节码(.class文件)加载为Class对象,所以需要编译好的.class文件。在Idea中Project中有个out目录,其中放的就是编译好的.class文件(只要你使用Idea运行过的.java文件)。
3. 代码
与原书逻辑基本一致,由于自己加入了多线程技术,所以新增了Servlet容器(使用了Map),并且该容器一开始就加载所有的Servlet。
HttpServer:
1 package note1; 2 3 import java.io.IOException; 4 import java.net.ServerSocket; 5 import java.net.Socket; 6 import java.util.concurrent.ExecutorService; 7 import java.util.concurrent.Executors; 8 9 /** 10 * Created by kanyuxia on 2017/4/24. 11 * HttpServer是模拟HTTP服务器:接受HTTP请求,响应静态资源或者Servlet资源 12 */ 13 public class HttpServer { 14 /** 15 * HttpServer端口号 16 */ 17 public static final int PORT = 10086; 18 /** 19 * Http静态文件根目录 20 */ 21 public static final String STATIC_RESOURCE_ROOT = "E:/java/HttpServer/staticresource"; 22 /** 23 * Http中Servlet文件根目录 24 */ 25 public static final String SERVLET_ROOT = "E:/java/HttpServer/servlet"; 26 /** 27 * 线程池 28 */ 29 private ExecutorService executorService; 30 /** 31 * 线程池大小 32 */ 33 public static final int THREAD_POOL_SIZE = 50; 34 35 HttpServer() { 36 executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE); 37 } 38 39 /** 40 * 启动HttpServer服务器 41 * 使用了try-with-resource: since jdk1.7 42 */ 43 public void start() { 44 // 创建ServerSocket 45 try (ServerSocket serverSocket = new ServerSocket(PORT)) { 46 while (true) { 47 // 客户端连接 48 Socket socket = serverSocket.accept(); 49 // 使用线程池处理该socket 50 ServerHandle serverHandle = new ServerHandle(socket); 51 executorService.execute(serverHandle); 52 } 53 } catch (IOException e) { 54 System.out.println(e); 55 } 56 } 57 }
Request:
1 package note1; 2 3 import javax.servlet.*; 4 import java.io.*; 5 import java.util.Enumeration; 6 import java.util.Locale; 7 import java.util.Map; 8 9 /** 10 * Created by kanyuxia on 2017/4/24 11 * Request类:实现了ServletRequest接口,如果HTTP请求是Servlet,则服务器创建ServletRequest和ServletResponse对象, 12 * 并传入Servlet的service()方法 13 */ 14 public class Request implements ServletRequest { 15 private final InputStream inputStream; 16 17 private String url; 18 19 public Request(InputStream inputStream) { 20 this.inputStream = inputStream; 21 } 22 23 public String getUrl() { 24 return url; 25 } 26 27 /** 28 * 解析请求URL 29 */ 30 public void parse() { 31 BufferedInputStream in = new BufferedInputStream(inputStream); 32 StringBuilder result = new StringBuilder(1024); 33 byte[] buffer = new byte[1024]; 34 int readNum = 0; 35 try { 36 readNum = in.read(buffer); 37 } catch (IOException e) { 38 e.printStackTrace(); 39 } 40 for (int i = 0; i < readNum; i++) { 41 result.append((char) buffer[i]); 42 } 43 // 解析URL 44 int start = result.toString().indexOf(" ") + 1; 45 int end = result.toString().indexOf(" ", start); 46 this.url = result.toString().substring(start, end); 47 } 48 49 50 @Override 51 public Object getAttribute(String name) { 52 return null; 53 } 54 55 @Override 56 public Enumeration<String> getAttributeNames() { 57 return null; 58 } 59 60 @Override 61 public String getCharacterEncoding() { 62 return null; 63 } 64 65 @Override 66 public void setCharacterEncoding(String env) throws UnsupportedEncodingException { 67 68 } 69 70 @Override 71 public int getContentLength() { 72 return 0; 73 } 74 75 @Override 76 public long getContentLengthLong() { 77 return 0; 78 } 79 80 @Override 81 public String getContentType() { 82 return null; 83 } 84 85 @Override 86 public ServletInputStream getInputStream() throws IOException { 87 return null; 88 } 89 90 @Override 91 public String getParameter(String name) { 92 return null; 93 } 94 95 @Override 96 public Enumeration<String> getParameterNames() { 97 return null; 98 } 99 100 @Override 101 public String[] getParameterValues(String name) { 102 return new String[0]; 103 } 104 105 @Override 106 public Map<String, String[]> getParameterMap() { 107 return null; 108 } 109 110 @Override 111 public String getProtocol() { 112 return null; 113 } 114 115 @Override 116 public String getScheme() { 117 return null; 118 } 119 120 @Override 121 public String getServerName() { 122 return null; 123 } 124 125 @Override 126 public int getServerPort() { 127 return 0; 128 } 129 130 @Override 131 public BufferedReader getReader() throws IOException { 132 return null; 133 } 134 135 @Override 136 public String getRemoteAddr() { 137 return null; 138 } 139 140 @Override 141 public String getRemoteHost() { 142 return null; 143 } 144 145 @Override 146 public void setAttribute(String name, Object o) { 147 148 } 149 150 @Override 151 public void removeAttribute(String name) { 152 153 } 154 155 @Override 156 public Locale getLocale() { 157 return null; 158 } 159 160 @Override 161 public Enumeration<Locale> getLocales() { 162 return null; 163 } 164 165 @Override 166 public boolean isSecure() { 167 return false; 168 } 169 170 @Override 171 public RequestDispatcher getRequestDispatcher(String path) { 172 return null; 173 } 174 175 @Override 176 public String getRealPath(String path) { 177 return null; 178 } 179 180 @Override 181 public int getRemotePort() { 182 return 0; 183 } 184 185 @Override 186 public String getLocalName() { 187 return null; 188 } 189 190 @Override 191 public String getLocalAddr() { 192 return null; 193 } 194 195 @Override 196 public int getLocalPort() { 197 return 0; 198 } 199 200 @Override 201 public ServletContext getServletContext() { 202 return null; 203 } 204 205 @Override 206 public AsyncContext startAsync() throws IllegalStateException { 207 return null; 208 } 209 210 @Override 211 public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { 212 return null; 213 } 214 215 @Override 216 public boolean isAsyncStarted() { 217 return false; 218 } 219 220 @Override 221 public boolean isAsyncSupported() { 222 return false; 223 } 224 225 @Override 226 public AsyncContext getAsyncContext() { 227 return null; 228 } 229 230 @Override 231 public DispatcherType getDispatcherType() { 232 return null; 233 } 234 }
Response:
1 package note1; 2 3 import javax.servlet.ServletOutputStream; 4 import javax.servlet.ServletResponse; 5 import java.io.*; 6 import java.net.URLConnection; 7 import java.util.Locale; 8 9 /** 10 * Created by kanyuxia on 2017/4/24. 11 */ 12 public class Response implements ServletResponse { 13 private final Request request; 14 15 private final OutputStream outputStream; 16 17 public Response(Request request, OutputStream outputStream) { 18 this.request = request; 19 this.outputStream = ou以上是关于《How Tomcat Works》读书笔记的主要内容,如果未能解决你的问题,请参考以下文章
How tomcat works 读书笔记十七 启动tomcat 上