Skip to content

Latest commit

 

History

History
579 lines (429 loc) · 21.5 KB

servlet-api源码赏析.md

File metadata and controls

579 lines (429 loc) · 21.5 KB

Servlet 基础

Servlet 简介

Servlet(Server Applet)是 J2EE 的内容之一,由 Java 编写的服务器端小程序。它是 web 请求的入口,主要功能在于交互式地(Request 和 Response)浏览和修改数据,生成动态 Web 内容。Servlet 运行于支持 Servlet 的 WEB 容器中,如 Tomcat。从实现上讲,Servlet 可以响应任何类型的请求,但绝大多数情况下 Servlet 只用来扩展基于 HTTP 协议 的 Web 服务器。servlet 的工作模式如下:

  • 客户端发送请求至 WEB 服务器;
  • 服务器根据请求的 URL 调用相应的 servlet 对象进行处理,获取到 servlet 对象的处理结果;
  • 服务器将 响应内容 返回给客户端;

另外,由于各种 MVC 框架的兴起,现在几乎没人会直接使用 servlet 来处理请求咯,往往都是把 SpringMVC 在 Tomcat 中一配,各种请求都交由 DispatcherServlet 来分发。

Servlet 生命周期

  • 加载 : 客户端第一次访问该 Servlet 时,Tomcat 会创建该 Servlet 的实例,一般只创建 1 次,所以 servlet 对象 在 Tomcat 是单例的;
  • 初始化 : Tomcat 调用 该 Servlet 的 init()方法 进行初始化;
  • 服务 : 每当客户端访问 该 Servlet 时,Tomcat 就会调用一次该 Servlet 的 service()方法 处理请求;
  • 销毁 : Tomcat 关闭时,会调用 这些 servlet 的 destroy()方法,让该实例释放掉所占的资源。

简单总结一下就是:只要访问 Servlet,service()方法 就会被调用,init()方法 只有第一次访问 Servlet 的时候才会被调用,destroy()方法 会在 Tomcat 关闭的时候被调用。

<load-on-startup>

在 web.xml 中配置 Servlet 时有个属性 <load-on-startup>1</load-on-startup>。翻译过来就是 “在启动时加载”,其作用如下:

  1. load-on-startup 元素标记容器是否应该在 web 应用程序 启动的时候就加载这个 Servlet,实例化并调用其 init()方法;
  2. 它的值必须是一个整数,表示 Servlet 被加载的先后顺序;
  3. 如果值为正整数或者 0 时,表示容器在应用启动时就加载并初始化这个 Servlet,值越小,Servlet 的优先级越高,就越先被加载。值相同时,容器就会自己选择顺序来加载;
  4. 如果该元素的值为负数或者没有设置,则容器会在 Servlet 被请求时才加载。

Servlet 的多线程并发问题

servlet 对象 在 Tomcat 服务器中 是 单实例-多线程并发访问的,比如 DispatcherServlet 对象 只会被创建一次,但多个请求同时过来时,Tomcat 线程池 的多个工作线程就会并发地访问该 DispatcherServlet 对象。所以,若一个 servlet 对象中存在被并发修改的共享数据(成员变量 等),又没有加锁控制并发安全,就很可能会出现线程安全问题。

解决方案:

  1. 把可能会并发修改的共享数据的代码块进行同步(使用 synchronized 或 Lock 对象);
  2. 建议在 Servlet 类 中尽量不要使用成员变量。若使用成员变量,则必须同步,并尽量缩小同步代码块的范围,以避免因为同步而导致并发效率降低。

Servlet 实现请求和响应

对于每次客户端请求,Web 容器 都会创建一个新的 HttpServletRequest 请求对象 和 一个新的 HttpServletResponse 响应对象,然后将这两个对象作为参数传递给相应的 Servlet 对象 的 service()方法,service()方法 再根据请求方式分别调用 doGet()/doPost()/doXXX()方法。经过一系列业务层处理,最后将结果封装到 response 对象中,交由 Socket 对象传输响应给客户端。

Servlet 源码解析

javax.servlet 包对 Servlet 规范 的一些主要行为和接口进行了定义和简单实现,它是 Servlet 规范 的体现,具体的实现交由下游厂商或开发者(如:Tomcat / Jetty),就像体现了 JDBC 规范的 java.sql 包 一样,主要负责定义标准和规范。其源码和注释如下。

publicinterfaceServlet { /** * 初始化servlet */publicabstractvoidinit(ServletConfigservletconfig) throwsServletException; /** * 提供服务 */publicabstractvoidservice(ServletRequestservletrequest, ServletResponseservletresponse) throwsServletException, IOException; /** * 销毁本servlet */publicabstractvoiddestroy(); publicabstractServletConfiggetServletConfig(); publicabstractStringgetServletInfo(); } /** * ServletConfig 主要用于保存一些 servlet的配置信息,与 servlet对象 成对出现 */publicinterfaceServletConfig { publicabstractStringgetServletName(); publicabstractServletContextgetServletContext(); publicabstractStringgetInitParameter(Strings); publicabstractEnumerationgetInitParameterNames(); } /** * 一个web应用对应一个ServletContext,ServletContext实例包含了所有servlet共享的资源信息 */publicinterfaceServletContext { publicabstractServletContextgetContext(Strings); publicabstractServletgetServlet(Strings) throwsServletException; publicabstractEnumerationgetServlets(); publicabstractEnumerationgetServletNames(); publicabstractintgetMajorVersion(); publicabstractSetgetResourcePaths(Strings); publicabstractURLgetResource(Strings) throwsMalformedURLException; publicabstractInputStreamgetResourceAsStream(Strings); publicabstractRequestDispatchergetRequestDispatcher(Strings); publicabstractRequestDispatchergetNamedDispatcher(Strings); publicabstractStringgetRealPath(Strings); publicabstractStringgetServerInfo(); publicabstractStringgetInitParameter(Strings); publicabstractEnumerationgetInitParameterNames(); publicabstractObjectgetAttribute(Strings); publicabstractEnumerationgetAttributeNames(); publicabstractvoidsetAttribute(Strings, Objectobj); publicabstractvoidremoveAttribute(Strings); publicabstractStringgetServletContextName(); } /** *定义了 Servlet 要处理的 Request请求信息,调用 Servlet 的 service()方法 时,会作为参数传入 */publicinterfaceServletRequest { publicabstractObjectgetAttribute(Strings); publicabstractEnumerationgetAttributeNames(); publicabstractStringgetCharacterEncoding(); publicabstractvoidsetCharacterEncoding(Strings) throwsUnsupportedEncodingException; publicabstractintgetContentLength(); publicabstractStringgetContentType(); publicabstractServletInputStreamgetInputStream() throwsIOException; publicabstractStringgetParameter(Strings); publicabstractEnumerationgetParameterNames(); publicabstractString[] getParameterValues(Strings); publicabstractMapgetParameterMap(); publicabstractStringgetProtocol(); publicabstractStringgetScheme(); publicabstractStringgetServerName(); publicabstractintgetServerPort(); publicabstractBufferedReadergetReader() throwsIOException; publicabstractStringgetRemoteAddr(); publicabstractStringgetRemoteHost(); publicabstractvoidsetAttribute(Strings, Objectobj); publicabstractvoidremoveAttribute(Strings); publicabstractLocalegetLocale(); publicabstractEnumerationgetLocales(); publicabstractbooleanisSecure(); publicabstractRequestDispatchergetRequestDispatcher(Strings); } /** * 定义了 Servlet 要返回的 Response响应信息,调用Servlet 的 service()方法 时,会作为参数传入 */publicinterfaceServletResponse { publicabstractStringgetCharacterEncoding(); publicabstractServletOutputStreamgetOutputStream() throwsIOException; publicabstractPrintWritergetWriter() throwsIOException; publicabstractvoidsetContentLength(inti); publicabstractvoidsetContentType(Strings); publicabstractvoidsetBufferSize(inti); publicabstractintgetBufferSize(); publicabstractvoidflushBuffer() throwsIOException; publicabstractvoidresetBuffer(); publicabstractbooleanisCommitted(); publicabstractvoidreset(); publicabstractvoidsetLocale(Localelocale); publicabstractLocalegetLocale(); }

其主要部分的类图 如下。

avatar

下面看一下 javax.servlet.http 包下的内容,它提供了很多 我经常用到的类和接口,比如:HttpServlet、HttpServletRequest、HttpServletResponse。其源码如下。

publicabstractclassHttpServletextendsGenericServletimplementsSerializable { privatestaticfinalStringMETHOD_DELETE = "DELETE"; privatestaticfinalStringMETHOD_HEAD = "HEAD"; privatestaticfinalStringMETHOD_GET = "GET"; privatestaticfinalStringMETHOD_OPTIONS = "OPTIONS"; privatestaticfinalStringMETHOD_POST = "POST"; privatestaticfinalStringMETHOD_PUT = "PUT"; privatestaticfinalStringMETHOD_TRACE = "TRACE"; privatestaticfinalStringHEADER_IFMODSINCE = "If-Modified-Since"; privatestaticfinalStringHEADER_LASTMOD = "Last-Modified"; privatestaticfinalStringLSTRING_FILE = "javax.servlet.http.LocalStrings"; privatestaticResourceBundlelStrings = ResourceBundle.getBundle("javax.servlet.http.LocalStrings"); /** * 将 请求及响应 转换成 HttpServletRequest 及 HttpServletResponse, * 再调用 service() 的重载方法 */publicvoidservice(ServletRequestreq, ServletResponseres) throwsServletException, IOException { HttpServletRequestrequest; HttpServletResponseresponse; try { request = (HttpServletRequest) req; response = (HttpServletResponse) res; } catch (ClassCastExceptione) { thrownewServletException("non-HTTP request or response"); } service(request, response); } /** * 对HTTP协议 各种类型的请求分别进行处理 */protectedvoidservice(HttpServletRequestreq, HttpServletResponseresp) throwsServletException, IOException { Stringmethod = req.getMethod(); if (method.equals("GET")) { longlastModified = getLastModified(req); if (lastModified == -1L) { doGet(req, resp); } else { longifModifiedSince = req.getDateHeader("If-Modified-Since"); if (ifModifiedSince < (lastModified / 1000L) * 1000L) { maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(304); } } } elseif (method.equals("HEAD")) { longlastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } elseif (method.equals("POST")) doPost(req, resp); elseif (method.equals("PUT")) doPut(req, resp); elseif (method.equals("DELETE")) doDelete(req, resp); elseif (method.equals("OPTIONS")) doOptions(req, resp); elseif (method.equals("TRACE")) { doTrace(req, resp); } else { StringerrMsg = lStrings.getString("http.method_not_implemented"); ObjecterrArgs[] = newObject[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(501, errMsg); } } protectedvoiddoGet(HttpServletRequestreq, HttpServletResponseresp) throwsServletException, IOException { Stringprotocol = req.getProtocol(); Stringmsg = lStrings.getString("http.method_get_not_supported"); if (protocol.endsWith("1.1")) resp.sendError(405, msg); elseresp.sendError(400, msg); } protectedlonggetLastModified(HttpServletRequestreq) { return -1L; } protectedvoiddoHead(HttpServletRequestreq, HttpServletResponseresp) throwsServletException, IOException { NoBodyResponseresponse = newNoBodyResponse(resp); doGet(req, response); response.setContentLength(); } protectedvoiddoPost(HttpServletRequestreq, HttpServletResponseresp) throwsServletException, IOException { Stringprotocol = req.getProtocol(); Stringmsg = lStrings.getString("http.method_post_not_supported"); if (protocol.endsWith("1.1")) resp.sendError(405, msg); elseresp.sendError(400, msg); } protectedvoiddoPut(HttpServletRequestreq, HttpServletResponseresp) throwsServletException, IOException { Stringprotocol = req.getProtocol(); Stringmsg = lStrings.getString("http.method_put_not_supported"); if (protocol.endsWith("1.1")) resp.sendError(405, msg); elseresp.sendError(400, msg); } protectedvoiddoDelete(HttpServletRequestreq, HttpServletResponseresp) throwsServletException, IOException { Stringprotocol = req.getProtocol(); Stringmsg = lStrings.getString("http.method_delete_not_supported"); if (protocol.endsWith("1.1")) resp.sendError(405, msg); elseresp.sendError(400, msg); } privateMethod[] getAllDeclaredMethods(Classc) { if (c.getName().equals("javax.servlet.http.HttpServlet")) returnnull; intj = 0; MethodparentMethods[] = getAllDeclaredMethods(c.getSuperclass()); MethodthisMethods[] = c.getDeclaredMethods(); if (parentMethods != null) { MethodallMethods[] = newMethod[parentMethods.length + thisMethods.length]; for (inti = 0; i < parentMethods.length; i++) { allMethods[i] = parentMethods[i]; j = i; } for (inti = ++j; i < thisMethods.length + j; i++) allMethods[i] = thisMethods[i - j]; returnallMethods; } else { returnthisMethods; } } protectedvoiddoOptions(HttpServletRequestreq, HttpServletResponseresp) throwsServletException, IOException { Methodmethods[] = getAllDeclaredMethods(getClass()); booleanALLOW_GET = false; booleanALLOW_HEAD = false; booleanALLOW_POST = false; booleanALLOW_PUT = false; booleanALLOW_DELETE = false; booleanALLOW_TRACE = true; booleanALLOW_OPTIONS = true; for (inti = 0; i < methods.length; i++) { Methodm = methods[i]; if (m.getName().equals("doGet")) { ALLOW_GET = true; ALLOW_HEAD = true; } if (m.getName().equals("doPost")) ALLOW_POST = true; if (m.getName().equals("doPut")) ALLOW_PUT = true; if (m.getName().equals("doDelete")) ALLOW_DELETE = true; } Stringallow = null; if (ALLOW_GET && allow == null) allow = "GET"; if (ALLOW_HEAD) if (allow == null) allow = "HEAD"; elseallow = allow + ", HEAD"; if (ALLOW_POST) if (allow == null) allow = "POST"; elseallow = allow + ", POST"; if (ALLOW_PUT) if (allow == null) allow = "PUT"; elseallow = allow + ", PUT"; if (ALLOW_DELETE) if (allow == null) allow = "DELETE"; elseallow = allow + ", DELETE"; if (ALLOW_TRACE) if (allow == null) allow = "TRACE"; elseallow = allow + ", TRACE"; if (ALLOW_OPTIONS) if (allow == null) allow = "OPTIONS"; elseallow = allow + ", OPTIONS"; resp.setHeader("Allow", allow); } protectedvoiddoTrace(HttpServletRequestreq, HttpServletResponseresp) throwsServletException, IOException { StringCRLF = "\r\n"; StringresponseString = "TRACE " + req.getRequestURI() + " " + req.getProtocol(); for (EnumerationreqHeaderEnum = req.getHeaderNames(); reqHeaderEnum.hasMoreElements();) { StringheaderName = (String) reqHeaderEnum.nextElement(); responseString = responseString + CRLF + headerName + ": " + req.getHeader(headerName); } responseString = responseString + CRLF; intresponseLength = responseString.length(); resp.setContentType("message/http"); resp.setContentLength(responseLength); ServletOutputStreamout = resp.getOutputStream(); out.print(responseString); out.close(); } privatevoidmaybeSetLastModified(HttpServletResponseresp, longlastModified) { if (resp.containsHeader("Last-Modified")) return; if (lastModified >= 0L) resp.setDateHeader("Last-Modified", lastModified); } } /** * 对 HTTP 请求信息进行定义 */publicinterfaceHttpServletRequestextendsServletRequest { publicstaticfinalStringBASIC_AUTH = "BASIC"; publicstaticfinalStringFORM_AUTH = "FORM"; publicstaticfinalStringCLIENT_CERT_AUTH = "CLIENT_CERT"; publicstaticfinalStringDIGEST_AUTH = "DIGEST"; publicabstractStringgetAuthType(); publicabstractCookie[] getCookies(); publicabstractlonggetDateHeader(Strings); publicabstractStringgetHeader(Strings); publicabstractEnumerationgetHeaders(Strings); publicabstractEnumerationgetHeaderNames(); publicabstractintgetIntHeader(Strings); publicabstractStringgetMethod(); publicabstractStringgetPathInfo(); publicabstractStringgetPathTranslated(); publicabstractStringgetContextPath(); publicabstractStringgetQueryString(); publicabstractStringgetRemoteUser(); publicabstractbooleanisUserInRole(Strings); publicabstractPrincipalgetUserPrincipal(); publicabstractStringgetRequestedSessionId(); publicabstractStringgetRequestURI(); publicabstractStringBuffergetRequestURL(); publicabstractStringgetServletPath(); publicabstractHttpSessiongetSession(booleanflag); publicabstractHttpSessiongetSession(); publicabstractbooleanisRequestedSessionIdValid(); publicabstractbooleanisRequestedSessionIdFromCookie(); publicabstractbooleanisRequestedSessionIdFromURL(); } /** * 对 HTTP 响应信息进行定义 */publicinterfaceHttpServletResponseextendsServletResponse { publicstaticfinalintSC_CONTINUE = 100; publicstaticfinalintSC_SWITCHING_PROTOCOLS = 101; publicstaticfinalintSC_OK = 200; publicstaticfinalintSC_CREATED = 201; publicstaticfinalintSC_ACCEPTED = 202; publicstaticfinalintSC_NON_AUTHORITATIVE_INFORMATION = 203; publicstaticfinalintSC_NO_CONTENT = 204; publicstaticfinalintSC_RESET_CONTENT = 205; publicstaticfinalintSC_PARTIAL_CONTENT = 206; publicstaticfinalintSC_MULTIPLE_CHOICES = 300; publicstaticfinalintSC_MOVED_PERMANENTLY = 301; publicstaticfinalintSC_MOVED_TEMPORARILY = 302; publicstaticfinalintSC_SEE_OTHER = 303; publicstaticfinalintSC_NOT_MODIFIED = 304; publicstaticfinalintSC_USE_PROXY = 305; publicstaticfinalintSC_TEMPORARY_REDIRECT = 307; publicstaticfinalintSC_BAD_REQUEST = 400; publicstaticfinalintSC_UNAUTHORIZED = 401; publicstaticfinalintSC_PAYMENT_REQUIRED = 402; publicstaticfinalintSC_FORBIDDEN = 403; publicstaticfinalintSC_NOT_FOUND = 404; publicstaticfinalintSC_METHOD_NOT_ALLOWED = 405; publicstaticfinalintSC_NOT_ACCEPTABLE = 406; publicstaticfinalintSC_PROXY_AUTHENTICATION_REQUIRED = 407; publicstaticfinalintSC_REQUEST_TIMEOUT = 408; publicstaticfinalintSC_CONFLICT = 409; publicstaticfinalintSC_GONE = 410; publicstaticfinalintSC_LENGTH_REQUIRED = 411; publicstaticfinalintSC_PRECONDITION_FAILED = 412; publicstaticfinalintSC_REQUEST_ENTITY_TOO_LARGE = 413; publicstaticfinalintSC_REQUEST_URI_TOO_LONG = 414; publicstaticfinalintSC_UNSUPPORTED_MEDIA_TYPE = 415; publicstaticfinalintSC_REQUESTED_RANGE_NOT_SATISFIABLE = 416; publicstaticfinalintSC_EXPECTATION_FAILED = 417; publicstaticfinalintSC_INTERNAL_SERVER_ERROR = 500; publicstaticfinalintSC_NOT_IMPLEMENTED = 501; publicstaticfinalintSC_BAD_GATEWAY = 502; publicstaticfinalintSC_SERVICE_UNAVAILABLE = 503; publicstaticfinalintSC_GATEWAY_TIMEOUT = 504; publicstaticfinalintSC_HTTP_VERSION_NOT_SUPPORTED = 505; publicabstractvoidaddCookie(Cookiecookie); publicabstractbooleancontainsHeader(Strings); publicabstractStringencodeURL(Strings); publicabstractStringencodeRedirectURL(Strings); publicabstractvoidsendError(inti, Strings) throwsIOException; publicabstractvoidsendError(inti) throwsIOException; publicabstractvoidsendRedirect(Strings) throwsIOException; publicabstractvoidsetDateHeader(Strings, longl); publicabstractvoidaddDateHeader(Strings, longl); publicabstractvoidsetHeader(Strings, Strings1); publicabstractvoidaddHeader(Strings, Strings1); publicabstractvoidsetIntHeader(Strings, inti); publicabstractvoidaddIntHeader(Strings, inti); publicabstractvoidsetStatus(inti); }
close