前言:
Servlet的框架是由两个Java包组成:javax.servlet和javax.servlet.http. 在javax.servlet包中定义了所有的Servlet类都必须实现或扩展的的通用接口和类.在javax.servlet.http包中定义了采用HTTP通信协议的HttpServlet类.
Servlet的框架的核心是javax.servlet.Servlet接口,所有的Servlet都必须实现这一接口.在Servlet接口中定义了5个方法,其中有3个方法代表了Servlet的声明周期:
init方法,负责初始化Servlet对象
service方法,负责相应客户的请求
destory方法,当Servlet对象退出声明周期时,负责释放占有的资源
当Web容器接收到某个Servlet请求时,Servlet把请求封装成一个HttpServletRequest对象,然后把对象传给Servlet的对应的服务方法.
HTTP的请求方式包括DELETE,GET,OPTIONS,POST,PUT和TRACE,在HttpServlet类中分别提供了相应的服务方法,它们是,doDelete(),doGet(),doOptions(),doPost(), doPut()和doTrace().
HttpServlet的功能
HttpServlet首先必须读取Http请求的内容。Servlet容器负责创建HttpServlet对象,并把Http请求直接封装到HttpServlet对象中,大大简化了HttpServlet解析请求数据的工作量。HttpServlet容器响应Web客户请求流程如下:
1)Web客户向Servlet容器发出Http请求;
2)Servlet容器解析Web客户的Http请求;
3)Servlet容器创建一个HttpRequest对象,在这个对象中封装Http请求信息;
4)Servlet容器创建一个HttpResponse对象;
5)Servlet容器调用HttpServlet的service方法,把HttpRequest和HttpResponse对象作为service方法的参数传给HttpServlet对象;
6)HttpServlet调用HttpRequest的有关方法,获取HTTP请求信息;
7)HttpServlet调用HttpResponse的有关方法,生成响应数据;
8)Servlet容器把HttpServlet的响应结果传给Web客户。
二、创建HttpServlet的步骤——“四部曲”
1)扩展HttpServlet抽象类;
2)覆盖HttpServlet的部分方法,如覆盖doGet()或doPost()方法;
3)获取HTTP请求信息。通过HttpServletRequest对象来检索HTML表单所提交的数据或URL上的查询字符串;
4)生成HTTP响应结果。通过HttpServletResponse对象生成响应结果,它有一个getWriter()方法,该方法返回一个PrintWriter对象。
- /**
- *
- * Copyright © 2017 http://blog.csdn.net/noseparte © Like the wind, like rain
- * @author Noseparte
- * @Comile 2017年12月8日--下午9:31:27
- * @Version 1.0
- * @Description HttpServlet源码分析
- */
- @SuppressWarnings("unused")
- public abstract class HttpServlet extends GenericServlet {
- private static final long serialVersionUID = 1L;
- private static final String METHOD_DELETE = "DELETE";
- private static final String METHOD_HEAD = "HEAD";
- private static final String METHOD_GET = "GET";
- private static final String METHOD_OPTIONS = "OPTIONS";
- private static final String METHOD_POST = "POST";
- private static final String METHOD_PUT = "PUT";
- private static final String METHOD_TRACE = "TRACE";
- private static final String HEADER_IFMODSINCE = "If-Modified-Since";
- private static final String HEADER_LASTMOD = "Last-Modified";
- private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
- private static final ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.http.LocalStrings");
- /**
- * 由服务器调用(通过服务方法)来允许servlet处理GET请求。
- *
- * 覆盖此方法以支持GET请求也会自动支持HTTP HEAD请求。HEAD请求是GET请求,在响应中不返回任何主体,只有请求头字段。
- *
- * 在覆盖该方法时,读取请求数据,编写响应头,获取响应的写入器或输出流对象,最后,编写响应数据。最好是包含内容类型和编码。当使用PrintWriter对象返回响应时,在访问PrintWriter对象之前设置内容类型。
- *
- * servlet容器必须在提交响应之前写头文件,因为在HTTP中,必须在响应主体之前发送头部。
- *
- * 在可能的情况下,标题设置content - length(ServletResponse.setContentLength(int)方法),允许servlet容器使用持久连接返回响应给客户端,改善性能。如果整个响应都在响应缓冲区内,则会自动设置内容长度。
- *
- * 当使用HTTP 1.1分块编码(这意味着响应有一个传输编码头)时,不要设置内容长度的标题。
- *
- * GET方法应该是安全的,也就是说,没有任何对用户负责的副作用。例如,大多数表单查询都没有副作用。如果客户机请求用于更改存储的数据,则请求应该使用其他的HTTP方法。
- *
- * GET方法也应该是幂等的,这意味着它可以安全地重复。有时,使方法安全也使其具有强大的功能。例如,重复查询既安全又有效,但在网上购买产品或修改数据既不安全,也不具有幂等功能。
- *
- * 如果请求格式不正确,doGet将返回HTTP "Bad Request" 消息。
- *
- * @param req
- * @param resp
- * @throws ServletException
- * @throws IOException
- */
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
- IOException {
- String protocol = req.getProtocol();
- String msg = lStrings.getString("http.method_get_not_supported");
- if (protocol.endsWith("1.1")) {
- resp.sendError(405, msg);
- } else {
- resp.sendError(400, msg);
- }
- }
- /**
- * 返回HttpServletRequest对象最后一次修改的时间,从1970年1月1日午夜开始,时间为毫秒。如果时间未知,该方法返回一个负数(默认值)。
- *
- * 支持HTTP GET请求的servlet可以快速确定它们的最后修改时间,应该覆盖这个方法。这使得浏览器和代理缓存更加有效,减少了服务器和网络资源的负载。
- *
- * @param req
- * @return
- */
- protected long getLastModified(HttpServletRequest req) {
- return - 1L;
- }
- /**
- * 从受保护的服务方法接收HTTP HEAD请求并处理请求。客户端发送一个HEAD请求,当它只想看到响应的头,例如内容类型或内容长度。HTTP HEAD方法计算响应中的输出字节,以便准确地设置内容长度标题。
- *
- * 如果您覆盖了这个方法,您可以避免计算响应主体,并直接设置响应标头以提高性能。请确保您所编写的doHead方法既安全又具有强大的功能(也就是说,保护自己不被多次调用一个HTTP头请求)。
- *
- * 如果HTTP HEAD请求格式不正确,doHead将返回HTTP "Bad Request" 消息。
- *
- * @param req
- * @param resp
- * @throws ServletException
- * @throws IOException
- */
- protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
- IOException {
- if (DispatcherType.INCLUDE.equals(req.getDispatcherType())) {
- doGet(req, resp);
- } else {
- NoBodyResponse response = new NoBodyResponse(resp);
- doGet(req, response);
- response.setContentLength();
- }
- }
- /**
- * 由服务器调用(通过服务方法)来允许servlet处理POST请求。HTTP POST方法允许客户端将无限长度的数据发送到Web服务器,并且在发布诸如信用卡号之类的信息时非常有用。
- *
- * 在覆盖该方法时,读取请求数据,编写响应头,获取响应的写入器或输出流对象,最后,编写响应数据。最好是包含内容类型和编码。当使用PrintWriter对象返回响应时,在访问PrintWriter对象之前设置内容类型。
- *
- * servlet容器必须在提交响应之前写头文件,因为在HTTP中,必须在响应主体之前发送头部。
- *
- * 在可能的情况下,标题设置content - length(ServletResponse.setContentLength(int)方法),允许servlet容器使用持久连接返回响应给客户端,改善性能。如果整个响应都在响应缓冲区内,则会自动设置内容长度。
- *
- * 当使用HTTP 1.1分块编码(这意味着响应有一个传输编码头)时,不要设置内容长度的标题。
- *
- * 这种方法不需要是安全的,也不需要强大的。通过POST请求的操作可以有副作用,例如,更新存储的数据或在线购买项目。
- *
- * 如果HTTP POST请求格式不正确,doPost将返回HTTP "Bad Request" 消息。
- *
- * @param req
- * @param resp
- * @throws ServletException
- * @throws IOException
- */
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
- IOException {
- String protocol = req.getProtocol();
- String msg = lStrings.getString("http.method_post_not_supported");
- if (protocol.endsWith("1.1")) {
- resp.sendError(405, msg);
- } else {
- resp.sendError(400, msg);
- }
- }
- /**
- * 由服务器调用(通过服务方法)来允许servlet处理PUT请求。PUT操作允许客户端将文件放在服务器上,类似于通过FTP发送文件。
- *
- * 在覆盖此方法时,保留与请求发送的任何内容头(包括内容长度、内容类型、内容转换编码、内容编码、内容基础、内容语言、内容位置、内容-md5和内容范围)。
- * 如果您的方法不能处理一个内容头,它必须发出一个错误消息(HTTP 501-没有实现)并丢弃该请求。有关HTTP 1.1的更多信息,请参阅RFC 2616。
- *
- * 这种方法不需要是安全的,也不需要强大的。doPut执行的操作可能会有一些副作用,用户可以对此负责。在使用这种方法时,可以在临时存储中保存受影响的URL的副本。
- *
- * 如果HTTP PUT请求格式不正确,则doPut将返回HTTP "Bad Request" 消息。
- *
- * @param req
- * @param resp
- * @throws ServletException
- * @throws IOException
- */
- protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
- IOException {
- String protocol = req.getProtocol();
- String msg = lStrings.getString("http.method_put_not_supported");
- if (protocol.endsWith("1.1")) {
- resp.sendError(405, msg);
- } else {
- resp.sendError(400, msg);
- }
- }
- /**
- * 由服务器调用(通过服务方法)来允许servlet处理一个删除请求。删除操作允许客户端从服务器中删除文档或Web页面。
- *
- * 这种方法不需要是安全的,也不需要强大的。通过删除所请求的操作可以有副作用,用户可以对此负责。在使用这种方法时,可以在临时存储中保存受影响的URL的副本。
- *
- * 如果HTTP DELETE请求格式不正确,doDelete将返回HTTP "Bad Request" 消息。
- *
- * @param req
- * @param resp
- * @throws ServletException
- * @throws IOException
- */
- protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
- IOException {
- String protocol = req.getProtocol();
- String msg = lStrings.getString("http.method_delete_not_supported");
- if (protocol.endsWith("1.1")) {
- resp.sendError(405, msg);
- } else {
- resp.sendError(400, msg);
- }
- }
- /**
- * 由服务器调用(通过服务方法)来允许servlet处理一个选项请求。选项请求确定服务器支持哪些HTTP方法,并返回一个适当的标头。
- * 例如,如果一个servlet覆盖doGet,该方法将返回以下标题:允许:GET,HEAD,TRACE,选项
- *
- * 除非servlet实现了新的HTTP方法,否则没有必要覆盖这个方法,除了HTTP 1.1实现的方法之外。
- *
- * @param c
- * @return
- */
- private static Method[] getAllDeclaredMethods(Class < ?>c) {
- if (c.equals(HttpServlet.class)) {
- return null;
- }
- Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass());
- Method[] thisMethods = c.getDeclaredMethods();
- if ((parentMethods != null) && (parentMethods.length > 0)) {
- Method[] allMethods = new Method[parentMethods.length + thisMethods.length];
- System.arraycopy(parentMethods, 0, allMethods, 0, parentMethods.length);
- System.arraycopy(thisMethods, 0, allMethods, parentMethods.length, thisMethods.length);
- thisMethods = allMethods;
- }
- return thisMethods;
- }
- /**
- * 由服务器调用(通过服务方法)来允许servlet处理一个选项请求。选项请求确定服务器支持哪些HTTP方法,并返回一个适当的标头。
- * 例如,如果一个servlet覆盖doGet,该方法将返回以下标题:允许:GET,HEAD,TRACE,选项
- *
- * 除非servlet实现了新的HTTP方法,否则没有必要覆盖这个方法,除了HTTP 1.1实现的方法之外。
- *
- * @param req
- * @param resp
- * @throws ServletException
- * @throws IOException
- */
- protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
- IOException {
- Method[] methods = getAllDeclaredMethods(getClass());
- boolean ALLOW_GET = false;
- boolean ALLOW_HEAD = false;
- boolean ALLOW_POST = false;
- boolean ALLOW_PUT = false;
- boolean ALLOW_DELETE = false;
- boolean ALLOW_TRACE = true;
- boolean ALLOW_OPTIONS = true;
- Class < ?>clazz = null;
- try {
- clazz = Class.forName("org.apache.catalina.connector.RequestFacade");
- Method getAllowTrace = clazz.getMethod("getAllowTrace", (Class[]) null);
- ALLOW_TRACE = ((Boolean) getAllowTrace.invoke(req, (Object[]) null)).booleanValue();
- } catch(ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException localClassNotFoundException) {}
- for (int i = 0; i < methods.length; i++) {
- Method m = 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;
- }
- }
- String allow = null;
- if (ALLOW_GET) {
- allow = "GET";
- }
- if (ALLOW_HEAD) {
- if (allow == null) {
- allow = "HEAD";
- } else {
- allow = allow + ", HEAD";
- }
- }
- if (ALLOW_POST) {
- if (allow == null) {
- allow = "POST";
- } else {
- allow = allow + ", POST";
- }
- }
- if (ALLOW_PUT) {
- if (allow == null) {
- allow = "PUT";
- } else {
- allow = allow + ", PUT";
- }
- }
- if (ALLOW_DELETE) {
- if (allow == null) {
- allow = "DELETE";
- } else {
- allow = allow + ", DELETE";
- }
- }
- if (ALLOW_TRACE) {
- if (allow == null) {
- allow = "TRACE";
- } else {
- allow = allow + ", TRACE";
- }
- }
- if (ALLOW_OPTIONS) {
- if (allow == null) {
- allow = "OPTIONS";
- } else {
- allow = allow + ", OPTIONS";
- }
- }
- resp.setHeader("Allow", allow);
- }
- /**
- * 由服务器调用(通过服务方法)来允许servlet处理跟踪请求。
- * 跟踪返回将跟踪请求发送到客户机的头,以便在调试中使用它们。没有必要覆盖这个方法。
- *
- * @param req
- * @param resp
- * @throws ServletException
- * @throws IOException
- */
- protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
- IOException {
- String CRLF = "\r\n";
- StringBuilder buffer = new StringBuilder("TRACE ").append(req.getRequestURI()).append(" ").append(req.getProtocol());
- Enumeration < String > reqHeaderEnum = req.getHeaderNames();
- while (reqHeaderEnum.hasMoreElements()) {
- String headerName = (String) reqHeaderEnum.nextElement();
- buffer.append(CRLF).append(headerName).append(": ").append(req.getHeader(headerName));
- }
- buffer.append(CRLF);
- int responseLength = buffer.length();
- resp.setContentType("message/http");
- resp.setContentLength(responseLength);
- ServletOutputStream out = resp.getOutputStream();
- out.print(buffer.toString());
- out.close();
- }
- /**
- * 从公共服务方法接收标准HTTP请求,并将它们分派到这个类中定义的doMethod方法。
- *
- * 该方法是servlet.service(javax.servlet的一个特定于http的版本。
- *
- * ServletRequest,javax.servlet.ServletResponse)方法。
- *
- * 没有必要覆盖这个方法。
- *
- * @param req
- * @param resp
- * @throws ServletException
- * @throws IOException
- */
- protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
- IOException {
- String method = req.getMethod();
- if (method.equals("GET")) {
- long lastModified = getLastModified(req);
- if (lastModified == -1L) {
- doGet(req, resp);
- } else {
- long ifModifiedSince;
- try {
- ifModifiedSince = req.getDateHeader("If-Modified-Since");
- } catch(IllegalArgumentException iae) {
- ifModifiedSince = -1L;
- }
- if (ifModifiedSince < lastModified / 1000L * 1000L) {
- maybeSetLastModified(resp, lastModified);
- doGet(req, resp);
- } else {
- resp.setStatus(304);
- }
- }
- } else if (method.equals("HEAD")) {
- long lastModified = getLastModified(req);
- maybeSetLastModified(resp, lastModified);
- doHead(req, resp);
- } else if (method.equals("POST")) {
- doPost(req, resp);
- } else if (method.equals("PUT")) {
- doPut(req, resp);
- } else if (method.equals("DELETE")) {
- doDelete(req, resp);
- } else if (method.equals("OPTIONS")) {
- doOptions(req, resp);
- } else if (method.equals("TRACE")) {
- doTrace(req, resp);
- } else {
- String errMsg = lStrings.getString("http.method_not_implemented");
- Object[] errArgs = new Object[1];
- errArgs[0] = method;
- errMsg = MessageFormat.format(errMsg, errArgs);
- resp.sendError(501, errMsg);
- }
- }
- private void maybeSetLastModified(HttpServletResponse resp, long lastModified) {
- if (resp.containsHeader("Last-Modified")) {
- return;
- }
- if (lastModified >= 0L) {
- resp.setDateHeader("Last-Modified", lastModified);
- }
- }
- /**
- * 将客户端请求发送到受保护的服务方法。没有必要覆盖这个方法。
- */
- public void service(ServletRequest req, ServletResponse res) throws ServletException,IOException {
- HttpServletResponse response;
- HttpServletRequest request;
- try {
- request = (HttpServletRequest) req;
- response = (HttpServletResponse) res;
- } catch(ClassCastException e) {
- throw new ServletException("non-HTTP request or response");
- }
- service(request, response);
- }
- }
转载自:Tomcat 架构分析(四) Servlet和Jsp模块