Servlet相关基础知识总结

Servlet复习

  1. 什么是Servlet?(简介、工作流程、Servlet容器工作流程)

    • 简介:

      • Servlet使用java编写的服务器端应用程序,主要功能是在于交互式的浏览和修改数据,生成动态的Web内容
      • 运行在服务端,用来处理客户端请求并响应给浏览器动态资源
      • 单实例,多线程
    • Servlet工作流程

      • 通过在web.xml中配置的url-pattern获取到servlet-name
      • 之后根据servlet-name去寻找对应的Servlet类
      • 接着找到对应的Servlet类之后,执行对应的service方法,根据请求处理和响应
      • 返回响应信息
    • Servlet容器(tomcat)整体的工作流程

      Servlet相关基础知识总结_第1张图片

  2. Servlet的核心API以及体系结构

    • 继承体系图:img

    • 核心API:

      • Servlet接口,定义了一系列的抽象方法.init(ServletConfig)、service(ServletReqeust,ServletResponse)、destroy()等是由容器调用的

        • init(ServletConfig):void
        • getServletConfig():ServletConfig
        • service(ServletReqeust,ServletResponse):void
        • getServletInfo():String
        • destroy():void
      • GenericServlet抽象类:

        • 实现了Servlet接口、ServletConfig接口、序列化Serializable接口,对Servlet接口提供通用实现,不与任何协议相关联。
        • GenericServlet抽象类实现了Servlet接口中的init(ServletConfig)方法,其中有一个ServletConfig的private实例,容器会使用它来初始化init(ServletConfig),该方法中调用空的init()方法,重写时需要注意添加super.init()
        • 实现GenericServlet类中全部方法。
      • HttpServlet抽象类:

        • HttpServlet类是GenericServlet类的子类。HttpServlet类为Serlvet接口提供了与HTTP协议相关的通用实现,也就是说,HttpServlet对象适合运行在与客户端采用HTTP协议通信的Servlet容器或者Web容器中。

        • 早期开发中,一般自定义Servlet类扩展字HttpServlet类

        • HttpServlet类实现了Servlet类接口中的Service(ServletRequest,ServletResponse)方法,实现方法中却是调用的它的重载方法

          public void service(ServletRequest req, ServletResponse res)
                  throws ServletException, IOException
              {
                  HttpServletRequest  request;
                  HttpServletResponse response;
                  
                  if (!(req instanceof HttpServletRequest &&
                          res instanceof HttpServletResponse)) {
                      throw new ServletException("non-HTTP request or response");
                  }
          
                  request = (HttpServletRequest) req;
                  response = (HttpServletResponse) res;
          
                  service(request, response);
              }
              
           protected void service(HttpServletRequest req, HttpServletResponse resp)
                  throws ServletException, IOException
              {
                  String method = req.getMethod();
          
                  if (method.equals(METHOD_GET)) {
                      long lastModified = getLastModified(req);
                      if (lastModified == -1) {
                          // servlet doesn't support if-modified-since, no reason
                          // to go through further expensive logic
                          doGet(req, resp);
                      } else {
                          long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                          if (ifModifiedSince < lastModified) {
                              // If the servlet mod time is later, call doGet()
                              // Round down to the nearest second for a proper compare
                              // A ifModifiedSince of -1 will always be less
                              maybeSetLastModified(resp, lastModified);
                              doGet(req, resp);
                          } else {
                              resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                          }
                      }
          
                  } else if (method.equals(METHOD_HEAD)) {
                      long lastModified = getLastModified(req);
                      maybeSetLastModified(resp, lastModified);
                      doHead(req, resp);
          
                  } else if (method.equals(METHOD_POST)) {
                      doPost(req, resp);
                      
                  } else if (method.equals(METHOD_PUT)) {
                      doPut(req, resp);
                      
                  } else if (method.equals(METHOD_DELETE)) {
                      doDelete(req, resp);
                      
                  } else if (method.equals(METHOD_OPTIONS)) {
                      doOptions(req,resp);
                      
                  } else if (method.equals(METHOD_TRACE)) {
                      doTrace(req,resp);
                      
                  } else {
                      //
                      // Note that this means NO servlet supports whatever
                      // method was requested, anywhere on this server.
                      //
          
                      String errMsg = lStrings.getString("http.method_not_implemented");
                      Object[] errArgs = new Object[1];
                      errArgs[0] = method;
                      errMsg = MessageFormat.format(errMsg, errArgs);
                      
                      resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
                  }
              }
              //重载service()方法中,首先调用HttpServletRequest类型的参数的getMethod()方法,获得客户端的请求方法,然后根据该请求方式来调用匹配的服务方法;如果为GET方式,则调用doGet()方法,如果为POST方式,则调用doPost()方法。
          
        • HttpServlet类为所有的请求方式,提供了默认的实现doGet(),doPost(),doPut(),doDelete()方法;这些方法的默认实现都会向客户端返回一个错误。

          
              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);
                  }
          
              }
          
              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);
                  }
          
              }
          
          
        • 对于HttpServlet类的具体实现类,一般会针对客户端的特定请求方法,覆盖HttpServlet类中的相应的doXXX方法。

          	protected void doGet(HttpServletRequest request, HttpServletResponse response)
          			throws ServletException, IOException {
          		doPost(request, response);
          	}
          
          	
          	
          	protected void doPost(HttpServletRequest request, HttpServletResponse response)
          			throws ServletException, IOException {
                  //xxx
              }
          
      • ServletRequest接口:

        • 简述:表示来自客户端的请求,当Servlet容器接收到客户端要求访问特定Servlet的请求时,容器先解析客户端的原始请求数据,把它包装成一个ServletRequest对象

        • 方法:

          getContentLength() —— 返回请求正文的长度,如果请求正文的长度未知,则返回-1getContentType() —— 获得请求正文的MIME类型,如果请求正文的类型为止,则返回null;
          getInputStream() —— 返回用于读取请求正文的输入流;
          getLocalAddr() —— 返回服务端的IP地址;
          getLocalName() —— 返回服务端的主机名;
          getLocalPort() —— 返回服务端的端口号;
          getParameters() —— 根据给定的请求参数名,返回来自客户请求中的匹配的请求参数值;
          getProtocal() —— 返回客户端与服务器端通信所用的协议名称及版本号;
          getReader() —— 返回用于读取字符串形式的请求正文的BufferReader对象;
          getRemoteAddr() —— 返回客户端的IP地址
          getRemoteHost() —— 返回客户端的主机名
          getRemotePort() —— 返回客户端的端口号
          
      • HttpServletRequest接口:

        • 简述:ServletRequest接口的子接口,用于读取HTTP请求中的相关信息。

        • 方法:

          getContextPath() —— 返回客户端请求方法的Web应用的URL入口,如客户端访问的URL为http://localhost:8080/hello/info,那么该方法返回“/hello”;
          getCookies() —— 返回HTTP请求中的所有Cookie;
          getHeader(String name) —— 返回HTTP请求头部的特定项;
          getHeaderName() —— 返回一个Enumeration对象,它包含了HTTP请求头部的所有项目名;
          getMethod() —— 返回HTTP请求方式;
          getRequestURL() —— 返回HTTP请求的头部的第一行中的URL;
          getQueryString() —— 返回HTTP请求中的查询字符串,即URL中的“?”后面的内容;
          
      • ServletResponse接口:{ServletResponse中响应正文的默认MIME类型是text/plain,即纯文本类型}

        • 简述:Servlet通过ServletResponse对象来生成响应结果.

        • 方法:

          setCharacterEncoding() —— 设置相应正文的字符编码。响应正文的默认字符编码为ISO-8859-1setContentLength() —— 设置响应正文的长度;
          setContentType() —— 设置响应正文的MIME类型;
          getCharacterEncoding() —— 获得响应正文的字符编码
          getContentType() —— 获得响应正文的MIME类型
          setBufferSize() —— 设置用于存放响应正文数据的缓冲区的大小
          getBufferSize() —— 获得用于存放响应正文数据的缓冲区的大小;
          reset() —— 清空缓冲区内的正文数据,并且清空响应状态代码及响应头
          resetBuffer() —— 仅仅清空缓冲区的正文数据,不清空响应状态代码及响应头;
          flushBuffer() —— 强制性地把缓冲区内的响应正文数据发送到客户端;
          isCommitted() —— 返回一个boolean类型的值,如果为true,表示缓冲区内的数据已经提交给客户,即数据已经发送到客户端;
          getOutputStream() —— 返回一个ServletOutputStream对象,Servlet用它来输出二进制的正文数据;
          getWriter() —— 返回一个PrinterWriter对象,Servlet用它来输出字符串形式的正文数据;
          
        • 其他:为了提高数据传输速率,数据会先被写入缓冲区中,当缓冲区中数据被提交到客户端后,ServletResponse的isComitted方法返回true,以下几种情况下,缓冲区内的数据会被提交给客户,即数据被发送到客户端:

          1. 当缓冲区内的数据已满时,ServletOutPutStream或PrintWriter会自动把缓冲区内的数据发送给客户端,并且清空缓冲区;
          2. Servlet调用ServletResponse对象的flushBuffer方法;
          3. Servlet调用ServletOutputStream或PrintWriter对象的flush方法或close方法;
        • 注意:为了确保SerlvetOutputStream或PrintWriter输出的所有数据都会被提交给客户,比较安全的做法是在所有数据都输出完毕后,调用ServletOutputStream或PrintWriter的close()方法,tomcat会自动关闭

        • 调用ServletResponse对象的setContentType()和setCharacterEncoding()方法可以设置MIME类型和字符编码,之后再调用ServletResponse的getOutputStream()或getWriter()方法,提交缓冲区内的正文数据;如此才使设置生效

      • HttpServletResponse接口:{HttpServletResponse中响应正文的默认MIME类型为text/html,即HTML文档类型}

        • 简述:提供了与HTTP协议相关的一些方法,Servlet可通过这些方法来设置HTTP响应头或向客户端写Cookie,其中也定义了一些代表HTTP响应状态代码的静态常量

        • 方法:

          addHeader() —— 向HTTP响应头中加入一项内容
          sendError() —— 向客户端发送一个代表特定错误的HTTP响应状态代码
          setHeader() —— 设置HTTP响应头中的一项内容,如果在响应头中已经存在这项内容,则原来的设置被覆盖
          setStatus() —— 设置HTTP响应的状态代码
          addCookie() —— 向HTTP响应中加入一个Cookie
          
      • ServletConfig接口:

        • 简介:当Servlet容器初始化一个Servlet对象时,会为这个Servlet对象创建一个ServletConfig对象,在Servlet对象中包含了Servlet的初始化参数信息。

        • 方法:

          getInitParameter(String name) —— 返回匹配的初始化参数值
          getInitParameterNames() —— 返回一个Enumeration对象,里面包含了所有的初始化参数名
          getServletContext() —— 返回一个ServletContext对象
          getServletName() —— 返回Servlet的名字,即web.xml文件中相应<servlet>元素的<servlet-name>子元素的值;如果没有为servlet配置<servlet-name>子元素,则返回Servlet类的名字
          
      • ServletContext接口:

        • 简介:Servlet与Servlet容器之间直接通信的接口,Servlet容器在启动一个Web应用时,会为它创建一个ServletContext对象。每个Web应用都有唯一的ServletContext对象,可以把ServletContext对象形象地理解为Web应用的管理者,同一个Web应用中的所有Servlet对象都共享一个ServletContext,Servlet对象可以通过其访问容器中的各种资源,如配置信息等。

        • 方法:

          setAttribute(String name, Object object) —— 把一个Java对象与一个属性名绑定,并存入到ServletContext中;
          getAttribute() —— 返回指定数姓名的属性值
          getAttributeNames() —— 返回一个Enumeration对象,包含所有存放在ServletContext中的属性名
          removeAttributes() —— 从ServletContext中删除匹配的属性
          
          getContextPath() —— 返回当前Web应用的URL入口
          getInitParameter() —— 返回Web应用范围内的匹配的初始化参数值。在web.xml中,直接在<web-app>根元素下定义的<context-param>元素表示应用范围内的初始化参数
          getServletContextName() —— 返回Web应用的名字,即web.xml文件中<display-name>元素的值
          getRequestDispatcher() —— 返回一个用于向其他WEB组件转发请求的RequestDispatcher对象
          
          getRealPath() —— 根据参数指定的虚拟路径,返回文件系统中的一个真实的路径
          getResources() —— 返回一个映射到参数指定的路径的URL
          getResourceAsStream() —— 返回一个用于读取参数指定的文件的输入流
          getMimeType() —— 返回参数指定的文件MIME类型
          
          log(String msg) —— 向Servlet的日志文件中写日志
          log(String message, Throwable throwable) —— 向Servlet的日志文件中写入错误日志,以及异常的堆栈信息
          
          
    • Servlet类以及其相关类:

      • 体系图:
        img
      • 与Servlet相关的类有三个,ServletConfig,ServletRequest和ServletResponse,它们都是通过容器传递给Servlet的;其中,ServletConfig是在Servlet初始化时传给Servlet的,后两个是在请求到达时调用Servlet传递过来的。
      • Request和Response流程:以TOMCAT为例,
        • tomcat接到请求首先将会创建org.apache.coyote.Request和org.apache.coyote.Response,这两个类是Tomcat内部使用的描述一次请求和相应的信息类,它们是一个轻量级的类。
        • 服务器接收到请求后,经过简单解析将这个请求快速分配给后续线程去处理。
        • 之后当交给一个用户线程去处理这个请求时又创建org.apache.catalina.connector.Request和org.apache.catalina.connector.Response对象。
        • 这两个对象一直贯穿整个Servlet容器直到要传给Servlet,传给Servlet的是Request和Response的Facade类。
        • img
  3. Servlet的生命周期?

    1. 会经过创建 、初始化、服务可用、服务不可用、处理请求、终止服务、销毁等其中状态
    2. 按照七种状态生命周期可以分为四个阶段:
      1. 加载和实例化
        1. 在服务器运行中,客户机首次向servlet发出请求时
        2. 重新装入Servlet时(如服务器重新启动、Servlet被修改)
        3. 在为Servlet配置了load-on-startup时,服务器在启动时自动装入此Servlet
      2. 初始化
        1. Servlet实例化后,Servlet调用Servlet的init(ServletConfig config)方法对Servlet进行初始化。此过程可以读取一些固定地数据、初始化JDBC的连接以及建立与其他资源的连接等操作。init()的方法参数ServletConfig对象由Servlet的容器创建并传递给Servlet,并在初始化完成后一直在内存中存在,直到Servlet被销毁。
      3. 处理请求
        1. 服务器接收到客户端的请求后,会为该请求创建一个“请求”对象和一个“响应”对象并调用service()方法,service()方法会在调用其他方法处理请求。Servlet的生命周期中,service()方法可能被多次调用,当多个客户端同时访问某个Servlet的service()方法时,服务器会为每个请求创建一个线程,这样可以并行处理多个请求,减少处理的等待时间,提高响应速度,但是也会导致对同一对象的并发访问问题。
      4. 销毁
        1. 当Servlet容器需要终止Servlet(如web服务器即将被关掉或需要让出资源),它会调用Servlet的destroy()方法使其释放正在使用的资源
  4. 实现一个简单的Servlet容器

    • 工作原理:用户向服务器发送一个请求时,http://hostname:port/contextpath/servletpath,hostname和port用来与服务器建立TCP连接,后面的URL用来选择在服务器中哪个子容器服务用户的请求,映射工作由专门的一个类完成:org.apache.tomcat.util.http.mapper,这个类保存了tomcat的container容器中的所有子容器的信息。org.apache.catalina.connector.Request类在进入Container容器之前,Mapper将会根据这次请求的hostname和contextpath将host和context容器设置到Request的mappingData属性中,所以当Request进入container容器之前,对于它要访问哪个子容器就已经确定。

    • 采用MVC框架的实现中,其基本原理是将所有的请求都映射到一个Servlet,然后去实现servie方法,这个方法也就是MVC框架的入口。

    • 实现一个简单的Servlet容器,源码见:简易tomcat

你可能感兴趣的:(java基础知识)