【0】README

0.1)本文部分文字转自 “深入剖析Tomcat”,旨在学习  一个简单的servlet容器  的基础知识;

0.2)for complete source code, please visit https://github.com/pacosonTang/HowTomcatWorks/tree/master/chapter2

0.3)温馨建议:建议阅读本文之前,已阅读过 tomcat(1)的系列文章,因为它们是环环相扣的;


【1】javax.servlet.Servlet接口

1)intro Servlet:Servlet编程需要使用到 javax.servlet 和 javax.servlet.http 两个包下的接口和类。在所有的类和接口中, javax.servlet.servlet 接口是最为重要的。所有的servlet 程序都必须实现该接口或继承自实现了该接口的类;
2)Servlet接口中声明了5个方法: init, service, destroy, getServletConfig, getServletInfo 方法;(干货——Servlet接口中声明了5个方法)
2.0)init, service, destroy方法是与 servlet的生命周期相关的方法;
2.1)init方法:当实例化某个servlet类后;servlet容器会调用其 init() 方法进行初始化,servlet只调用该方法一次;且servlet 程序员可以覆盖此方法,在其中编写仅需要执行一次的初始化代码;
2.2)service方法:当一个servlet客户端请求到达后,servlet容器就调用相应的servlet的service方法,并将 servletRequest 和 servletResponse 对象作为参数传入;
2.3)destroy方法:当servlet容器关闭或servlet容器要释放内存时,才会将 servlet实例移除,而且只有当servlet实例的service方法中的所有线程都退出或执行超时后,才会调用 destroy方法;
3)看个荔枝:
public class PrimitiveServlet implements Servlet {public void init(ServletConfig config) throws ServletException {System.out.println("init");}public void service(ServletRequest request, ServletResponse response)throws ServletException, IOException {System.out.println("from service");PrintWriter out = response.getWriter();out.println("Hello. Roses are red.");out.print("Violets are blue.");}public void destroy() {System.out.println("destroy");}public String getServletInfo() {return null;}public ServletConfig getServletConfig() {return null;}
}
【2】应用程序1
1)一个功能齐全的 servlet容器有以下几件事情要做(things):(干货——servlet容器需要做的事情)
t1)当第一次调用某个 servlet 时,要载入该 servlet类,并调用其 init() 方法;(干货——servlet仅调用该方法一次)
t2)针对每个request请求, 创建一个 javax.servlet.ServletRequest 实例 和 一个 javax.servlet.ServletResponse 实例;
t3)调用该 servlet 的 service() 方法,将 servletRequest对象和 servletResponse对象作为参数传入;(参见上述PrimitiveServlet的定义)
t4)当关闭该servlet类时,调用其 destroy() 方法,并卸载该servlet类;
2)servlet容器的实现源码和访问结果
2.1)printing results
2.2)soruce code at a glance
// HttpServer1 类 既可以对静态资源请求,也可以对servlet资源请求
public class HttpServer1 { private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";  private boolean shutdown = false;public static void main(String[] args) {HttpServer1 server = new HttpServer1();server.await();}public void await() {ServerSocket serverSocket = null;int port = 8080;try {serverSocket =  new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));}catch (IOException e) {e.printStackTrace();System.exit(1);}// Loop waiting for a requestwhile (!shutdown) {Socket socket = null;InputStream input = null;OutputStream output = null;try {socket = serverSocket.accept();input = socket.getInputStream();output = socket.getOutputStream();// create Request object and parseRequest request = new Request(input);request.parse();// create Response objectResponse response = new Response(output);response.setRequest(request);// check if this is a request for a servlet or a static resource// a request for a servlet begins with "/servlet/"if (request.getUri().startsWith("/servlet/")) { // 若HTTP请求的是servlet(以servlet打头)ServletProcessor1 processor = new ServletProcessor1();processor.process(request, response);} // 若http请求的是静态资源else {StaticResourceProcessor processor = new StaticResourceProcessor();processor.process(request, response);}// Close the socketsocket.close();//check if the previous URI is a shutdown commandshutdown = request.getUri().equals(SHUTDOWN_COMMAND);}catch (Exception e) {e.printStackTrace();System.exit(1);}}}
}
package com.tomcat.chapter2;import java.io.File;public class Constants { // 常数类,封装了 WEB_ROOT 常量public static final String WEB_ROOT =System.getProperty("user.dir") + File.separator  + "webroot";
}
// Request: HTTP请求对象,其parse方法用于读取HTTP请求头;其parseUri方法用于解析client 请求的 uri
public class Request implements ServletRequest {private InputStream input;private String uri;public Request(InputStream input) {this.input = input;}public String getUri() {return uri;}private String parseUri(String requestString) {int index1, index2;index1 = requestString.indexOf(' ');if (index1 != -1) {index2 = requestString.indexOf(' ', index1 + 1);if (index2 > index1)return requestString.substring(index1 + 1, index2);}return null;}public void parse() {// Read a set of characters from the socketStringBuffer request = new StringBuffer(2048);int i;byte[] buffer = new byte[2048];try {i = input.read(buffer);}catch (IOException e) {e.printStackTrace();i = -1;}for (int j=0; j<i; j++) {request.append((char) buffer[j]);}System.out.print(request.toString());uri = parseUri(request.toString());}//...... some barren functions
// Response:<span style="color: rgb(85, 85, 85); font-family: 宋体;">HTTP响应对象,其sendStaticResource方法 读取request解析出的uri对应的资源文件,并发送该文件数据到client端;
public class Response implements ServletResponse {private static final int BUFFER_SIZE = 1024;Request request;OutputStream output;PrintWriter writer;public Response(OutputStream output) {this.output = output;}public void setRequest(Request request) {this.request = request;}/* This method is used to serve a static page */public void sendStaticResource() throws IOException {byte[] bytes = new byte[BUFFER_SIZE];FileInputStream fis = null;try {/* request.getUri has been replaced by request.getRequestURI */File file = new File(Constants.WEB_ROOT, request.getUri());fis = new FileInputStream(file);/*HTTP Response = Status-Line*(( general-header | response-header | entity-header ) CRLF)CRLF[ message-body ]Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF*/int ch = fis.read(bytes, 0, BUFFER_SIZE);while (ch!=-1) {output.write(bytes, 0, ch);ch = fis.read(bytes, 0, BUFFER_SIZE);}}catch (FileNotFoundException e) {String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +"Content-Type: text/html\r\n" +"Content-Length: 23\r\n" +"\r\n" +"<h1>File Not Found</h1>";output.write(errorMessage.getBytes());}finally {if (fis!=null)fis.close();}}/** implementation of ServletResponse  */public PrintWriter getWriter() throws IOException {// autoflush is true, println() will flush,// but print() will not.writer = new PrintWriter(output, true);return writer;}
// (servlet 处理器)用于处理对servlet 资源的HTTP 请求(干货代码,特别是里面的类加载器)
public class ServletProcessor1 {public void process(Request request, Response response) {String uri = request.getUri();String servletName = uri.substring(uri.lastIndexOf("/") + 1);URLClassLoader loader = null; // 类载入器try {// create a URLClassLoader, 创建类载入器(类加载器是干货代码 )URL[] urls = new URL[1];URLStreamHandler streamHandler = null;File classPath = new File(Constants.WEB_ROOT);// the forming of repository is taken from the createClassLoader method in// org.apache.catalina.startup.ClassLoaderFactoryString repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;// file:E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\webroot\// the code for forming the URL is taken from the addRepository method in// org.apache.catalina.loader.StandardClassLoader class.urls[0] = new URL(null, repository, streamHandler);// urls[0] = file:E:/bench-cluster/cloud-data-preprocess/HowTomcatWorks/webroot/loader = new URLClassLoader(urls);}catch (IOException e) {System.out.println(e.toString() );}Class myClass = null;try {myClass = loader.loadClass("servlet."+servletName); // 载入 servlet类}catch (ClassNotFoundException e) {System.out.println(e.toString());}Servlet servlet = null;try {servlet = (Servlet) myClass.newInstance(); // 会创建已载入的servlet类的一个实例servlet.service((ServletRequest) request, (ServletResponse) response);}catch (Exception e) {System.out.println(e.toString());}catch (Throwable e) {System.out.println(e.toString());}}
}
public class StaticResourceProcessor {public void process(Request request, Response response) {try {response.sendStaticResource(); // 直接调用response的 sendStaticResource 方法.....}catch (IOException e) {e.printStackTrace();}}
}


补充)本文总结了上述应用程序的调用流程图

【3】应用程序2
1)problem:ServletProcessor1 中必须将Request 转型为 javax.servlet.ServletRequest,将Response 转型为 javax.servlet.ServletResponse 实例,将它们作为参数传递给 service方法;
1.1)这是不安全的做法: 一旦我们有了Request 实例后,我们就可以调用其parse方法,而有了Response实例后,我们就可以调用其 sendStaticResource方法。不能将parse() 和 sendStaticResource() 设置为私有方法,因为它们会被其他类调用,但这两个方法在servlet中不应该是可用的;
2)solution:使用外观类(Facade)来解决
2.1)构建外观类 RequestFacade 和 ResponseFacade:分别实现 ServletRequest 和 ServletResponse ,然后在其构造函数中指定Request 和 Response对象(private私有访问权限) ,外观类中的实现方法均调用 Request 或 Response对象的相应方法;
3)Conclusion:引入外观类的目的是, 不向外界提供访问到Request 和 Response 的接口;(干货——引入外观类的目的是, 不向外界提供访问到Request 和 Response 的接口)
4)源代码实现和打印结果
4.1)打印结果同 HttpServer1,故 omit it;
4.2)源代码实现(我这里给给出外观类代码,其他和HttpServer1 中的类似)
public class HttpServer2 {// shutdown commandprivate static final String SHUTDOWN_COMMAND = "/SHUTDOWN";// the shutdown command receivedprivate boolean shutdown = false;public static void main(String[] args) {HttpServer2 server = new HttpServer2();server.await();}public void await() {ServerSocket serverSocket = null;int port = 8080;try {serverSocket =  new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));}catch (IOException e) {e.printStackTrace();System.exit(1);}// Loop waiting for a requestwhile (!shutdown) {Socket socket = null;InputStream input = null;OutputStream output = null;try {socket = serverSocket.accept();input = socket.getInputStream();output = socket.getOutputStream();// create Request object and parseRequest request = new Request(input);request.parse();// create Response objectResponse response = new Response(output);response.setRequest(request);//check if this is a request for a servlet or a static resource//a request for a servlet begins with "/servlet/"if (request.getUri().startsWith("/servlet/")) {ServletProcessor2 processor = new ServletProcessor2();processor.process(request, response);}else {StaticResourceProcessor processor = new StaticResourceProcessor();processor.process(request, response);}// Close the socketsocket.close();//check if the previous URI is a shutdown commandshutdown = request.getUri().equals(SHUTDOWN_COMMAND);}catch (Exception e) {e.printStackTrace();System.exit(1);}}}
}

public class RequestFacade implements ServletRequest {private ServletRequest request = null;public RequestFacade(Request request) {this.request = request;}/* implementation of the ServletRequest*/public Object getAttribute(String attribute) {return request.getAttribute(attribute);}public Enumeration getAttributeNames() {return request.getAttributeNames();}public String getRealPath(String path) {return request.getRealPath(path);}public RequestDispatcher getRequestDispatcher(String path) {return request.getRequestDispatcher(path);}public boolean isSecure() {return request.isSecure();}public String getCharacterEncoding() {return request.getCharacterEncoding();}public int getContentLength() {return request.getContentLength();}public String getContentType() {return request.getContentType();}public ServletInputStream getInputStream() throws IOException {return request.getInputStream();}public Locale getLocale() {return request.getLocale();}public Enumeration getLocales() {return request.getLocales();}public String getParameter(String name) {return request.getParameter(name);}public Map getParameterMap() {return request.getParameterMap();}public Enumeration getParameterNames() {return request.getParameterNames();}public String[] getParameterValues(String parameter) {return request.getParameterValues(parameter);}public String getProtocol() {return request.getProtocol();}public BufferedReader getReader() throws IOException {return request.getReader();}public String getRemoteAddr() {return request.getRemoteAddr();}public String getRemoteHost() {return request.getRemoteHost();}public String getScheme() {return request.getScheme();}public String getServerName() {return request.getServerName();}public int getServerPort() {return request.getServerPort();}public void removeAttribute(String attribute) {request.removeAttribute(attribute);}public void setAttribute(String key, Object value) {request.setAttribute(key, value);}public void setCharacterEncoding(String encoding)throws UnsupportedEncodingException {request.setCharacterEncoding(encoding);}}
public class ResponseFacade implements ServletResponse {private ServletResponse response;public ResponseFacade(Response response) {this.response = response;}public void flushBuffer() throws IOException {response.flushBuffer();}public int getBufferSize() {return response.getBufferSize();}public String getCharacterEncoding() {return response.getCharacterEncoding();}public Locale getLocale() {return response.getLocale();}public ServletOutputStream getOutputStream() throws IOException {return response.getOutputStream();}public PrintWriter getWriter() throws IOException {return response.getWriter();}public boolean isCommitted() {return response.isCommitted();}public void reset() {response.reset();}public void resetBuffer() {response.resetBuffer();}public void setBufferSize(int size) {response.setBufferSize(size);}public void setContentLength(int length) {response.setContentLength(length);}public void setContentType(String type) {response.setContentType(type);}public void setLocale(Locale locale) {response.setLocale(locale);}}
public class ServletProcessor2 {public void process(Request request, Response response) {String uri = request.getUri();String servletName = uri.substring(uri.lastIndexOf("/") + 1);URLClassLoader loader = null;try {URL[] urls = new URL[1];URLStreamHandler streamHandler = null;File classPath = new File(Constants.WEB_ROOT);String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;urls[0] = new URL(null, repository, streamHandler);loader = new URLClassLoader(urls);}catch (IOException e) {System.out.println(e.toString() );}Class myClass = null;try {myClass = loader.loadClass("servlet." + servletName);}catch (ClassNotFoundException e) {System.out.println(e.toString());}Servlet servlet = null;RequestFacade requestFacade = new RequestFacade(request);  // attend this lineResponseFacade responseFacade = new ResponseFacade(response); <span style="font-family: 宋体;">// attend this line</span>try {servlet = (Servlet) myClass.newInstance();servlet.service((ServletRequest) requestFacade, (ServletResponse) responseFacade); // and this line}catch (Exception e) {System.out.println(e.toString());}catch (Throwable e) {System.out.println(e.toString());}}
}

tomcat(2)一个简单的servlet容器相关推荐

  1. 2.一个简单的Servlet容器

    章前准备     如何处理ifelse众多的问题,当然也可以说是switch case     我们经常写这样的代码,如果这货小一点,且可预测什么的,那都不是事,问题是如果他要是可拓展的类- -!让他 ...

  2. java nio servlet_java nio http服务器(3)简单的Servlet容器

    Servlet容器中放着我们所有要访问的Servlet,根据我们具体的请求来访问不同的Servlet.下面是一个简单的Servlet容器的实现.request和response的封装以及连接器的实现代 ...

  3. 探秘Tomcat——一个简易的Servlet容器

    即便再简陋的服务器也是服务器,今天就来循着书本的第二章来看看如何实现一个servlet容器. 背景知识 既然说到servlet容器这个名词,我们首先要了解它到底是什么. servlet 相比你或多或少 ...

  4. 手写一个简单的IOC容器

    手写一个简单的IOC容器 原文 http://localhost:4000/2020/02/25/SSM/spring/%E6%89%8B%E5%86%99%E4%B8%80%E4%B8%AA%E5% ...

  5. 手写Spring-第一章-实现一个简单的Bean容器

    前言 开个新坑,来整点儿大项目.有这个想法是因为用了那么久的Spring,但是某一天突然冒出来一个念头:Spring到底是怎么实现这些功能的.发现脑子一片空白.在我抽出纸巾擦干脑门儿上的汗之后,我决定 ...

  6. java servlet例子_Servlet学习教程(三)---- 一个简单的Servlet例子

    我们用个最简单的Servlet例子来解说一下Servlet简单配置以及Servlet类实现类的写法. 第一,我们新建一个Dynamic Web Project,起名Servlet 点击NEXT,设置D ...

  7. Servlet学习DAY_01:服务器概念/Web服务器的作用/ Servlet概念/ 如何关联和解除Tomcat/ 创建一个Web工程 /Servlet响应流程/ Get-Post /常见异常

    Servlet 什么是服务器 服务器就是一台高性能电脑 电脑上安装了提供服务的软件就称为 xxx服务器 举例: 邮件服务器: 就是在电脑上安装了提供邮件收发服务的软件 ftp服务器: 就是在电脑上安装 ...

  8. Linux Namespace系列(09):利用Namespace创建一个简单可用的容器

    本文将演示如何利用namespace创建一个完整的容器,并在里面运行busybox.如果对namespace不是很熟悉,请先参考前面几遍介绍不同类型namespace的文章. busybox是一个Li ...

  9. tomcat(1)一个简单的web server

    [0]README 0.1)本文部分描述转自"深入剖析tomcat", 旨在学习  一个简单的web server  的基础知识: 0.2)for complete source ...

最新文章

  1. Mac 使用Android Studio查找数字签名的方法SHA1或MD5
  2. LeetCode Super Ugly Number
  3. php用什么做缓存文件格式,怎么用php语言来做文件缓存
  4. php笔记之表单验证
  5. Windows 10下,如何使用PowerShell批量重启局域网电脑
  6. GHOSTXP_SP3电脑公司快速安装机版V2013
  7. staruml透明_第05组 团队项目-需求分析报告
  8. 02 掌握变量的定义和使用方法 1214
  9. 【模型压缩】通道剪枝《Pruning Filters For Efficient ConvNets》论文翻译
  10. 迷宫问题 (dfs)
  11. windows 2008R2鼠标移动到任务栏一直是漏斗状态
  12. 电信wifi服务器不响应,电信老员工告诉你,为什么你家WiFi信号满格却上不了网?...
  13. 蓝桥杯c语言大一的知识够了吗,参加蓝桥杯后的感受以及个人总结
  14. Python修改图片分辨率
  15. 在线扑克运营商在 WSOP 赛事开幕当天遭到 DDoS 攻击
  16. 软件测试笔记——3.多种多样的测试类型
  17. 如何清洁AirPods、AirPods Pro、AirPods Max 和 EarPods?
  18. 传奇服务器端如何修改称号,图文解说传奇人物称号设置详细步骤
  19. 清除系统垃圾的bat文件
  20. 时间序列转二维图像方法及其应用研究综述

热门文章

  1. A Hard Problem
  2. P2852 [USACO06DEC]Milk Patterns G
  3. CodeForces 1396E Distance Matching(构造+树的重心+dfs+set)
  4. P5081 Tweetuzki爱取球(期望)(线性求逆元)
  5. 震惊!快速幂怎么编?省一说暴力,银牌说递归,国集听完笑了
  6. P4001-[ICPC-Beijing 2006]狼抓兔子【对偶图】
  7. P5341-[TJOI2019]甲苯先生和大中锋的字符串【SAM】
  8. CF1114F-Please, another Queries on Array?【线段树,欧拉函数】
  9. jzoj2908,P1527-[集训队互测 2012]矩阵乘法【整体二分,二维树状数组】
  10. ssl2294-打包【dp练习】