专栏首页FoamValueServlet API 源码剖析

Servlet API 源码剖析

摘要:Servlet 存在于各个 Web 应用程序之中,承担着用户浏览器与服务器之间的信息交互,是一个容易忽视又非常重要的软件程序。互联网时代的当下,大到某宝、小到V商都无时无刻不使用着 Servlet。

从目录出发

从目录出发,整个源代码分为 servlet、http、descriptor、annotation 四个部分。

其中 servlet 部分是最重要的,它是定义了与 Servlet 容器相关的 Java Servlet 接口、类。http 部分是第二重要的,它承接了 HTTP 协议相关的 Java Servlet 实现。剩下的 descriptor、annotation 部分虽然也重要,但相对来说比较简单。descriptor 部分用于从 web.xml、web-fragment.xml 中获取 web 应用配置信息的功能。annotation 部分用于注解的方式替换在 web.xml 中的繁琐配置。

这里要提下 Servlet 的两次重要版本更新。

首先是 Servlet 3.0 版本带来的新特性(如下列表)。descriptor、annotation 部分也就是这个时候引入 Servlet 之中的。

  1. 注解支持:允许使用注解的方式,来替换原来在 web.xml 中的 Servlet、Listener、Filter 配置。
  2. web 模块化:允许 Servlet、Listener、Filter 类文件单独打包成 jar 包,放在 WEB-INF\lib 目录下,配合 web-fragment.xml 使用。
  3. Servlet 异步处理:支持异步调用。
  4. 优化文件上传 API:通过 HttpServletRequest 获取到的 Part 对象,可以直接操作文件域。

其次是 Servlet 4.0 版本带来的新特性(如下列表)。

  1. 服务器推送:服务器能预测客户端需要的资源,在完成请求处理之前,将这些资源推送到客户端。
  2. 将服务器推送与 JSF 页面结合:当 JSF 引擎在处理和呈现页面时,这些资源将被逐个推送到客户端。
  3. ServletMapping 接口:服务器可以对 Servlet 的 URL 执行运行时检查。

综上内容,基本涵盖了 Servlet 的主要功能内容。

从生命周期出发

Servlet 非常重要,那它的一生是怎么样的呢?

public interface Servlet {
    public void init(ServletConfig config) throws ServletException;
    public ServletConfig getServletConfig();
    public void service(ServletRequest req, ServletResponse res)
  throws ServletException, IOException;
    public String getServletInfo();
    public void destroy();
}

从 Servlet 接口开始。当 Web 程序启动的时候,Servlet 容器会调用 init() 方法进行初始化。Web 程序启动成功之后,Servlet 会一直运行在 Servlet 容器之中。当浏览器需要请求服务端时,Servlet 容器会调用 service() 方法进行请求处理、并相应结果返回。当 Web 程序关闭时,Servlet 容器会调用 destroy() 方法标记 Servlet 对象可以被垃圾回收。

从作用域出发

从作用域出发,Servlet 中常用来存储数据的三大作用域,按使用范围从小到大分别为:HttpServletRequest、HttpSession、ServletContext。

HttpServletRequest 的使用范围是从浏览器请求开始,到这次请求服务端响应后结束。

HttpSession 的使用范围是从服务端第一次调用 getSession 方法开始,到 session 有效时间内(默认为 30 min)结束。它的结束时间会因为服务端异常、服务端重启、服务端手动注销等情况提前失效。

ServletContext 的使用范围是从 Servlet 初始化开始,伴随整个 Servlet 生命周期。

从源码出发

ServletConfig

Servlet 在 Servlet 容器初始化时,读取 web.xml 中的 Servlet 配置信息。

public String getServletName();
public ServletContext getServletContext();
public String getInitParameter(String name);
public Enumeration<String> getInitParameterNames();

ServletConfig 接口提供的方法,分别是获取 Servlet 名称、ServletContext、指定 name 的初始化数据,以及列举所有的初始化数据名称。

ServletConfig 接口的实现类,分别是直接实现 ServletConfig 接口的 GenericServlet 类,以及继承了 GenericServlet 类的 HttpServlet 类。依照类的命名 GenericServlet 是通用的协议无关的接口方法实现,而 HttpServlet 继承通用、协议无关的基础上,扩展了 Http 协议相关的方法。

GenericServlet

public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable {

  private transient ServletConfig config;

  public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
  }

  public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;

GenericServlet 类自身拥有一个 ServletConfig 接口对象。也就说 GenericServlet 同时也支持其他自定义的 ServletConfig 接口实现类。

GenericServlet 抽象类是 Servlet 的适配器,也就是 Java 设计模式中的适配器模式。

当程序员需要自定义 MyServlet 类的时候,只需要继承 GenericServlet 抽象类,并实现 service() 抽象方法即可。并不需要去关注 Servlet、ServletConfig 接口的其他方法,应该如何实现。

HttpServlet

@Override
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) {
      doGet(req, resp);
    } else {
      long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
      if (ifModifiedSince < lastModified) {
        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 {
    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);
  }
}

HttpServlet 抽象类,直接把 service() 方法根据请求方式 req.getMethod() 的不同,拆分成 doPost()、doPut()、doDelete() 等不同且具体的方法。

当程序员想要实现一个浏览器 GET 请求时,只需要继承 HttpServlet 抽象类并实现 doGet() 方法即可。

ServletContext

在 Servlet 容器启动时,会创建 ServletContext 代表当前整个生命周期。

从 3.0 版本开始,支持在运行时动态部署 Servlet、过滤器、监听器,以及为 Servlet 和过滤器增加 URL 映射等。

从 4.0 版本开始,支持在运行时动态添加 jsp 文件,以及提供了改变默认请求字符编码与 session 超时的处理接口。

ServletContextListener

default public void contextInitialized(ServletContextEvent sce) {}
default public void contextDestroyed(ServletContextEvent sce) {}

用于接收有关 ServletContext 生命周期更改的通知事件的接口。

ServletContextAttributeListener

default public void attributeAdded(ServletContextAttributeEvent event) {}default public void attributeRemoved(ServletContextAttributeEvent event) {}default public void attributeReplaced(ServletContextAttributeEvent event) {}

用于接收有关 ServletContext 属性更改的通知事件的接口。

ServletContextEvent

default public void attributeAdded(ServletContextAttributeEvent event) {}
default public void attributeRemoved(ServletContextAttributeEvent event) {}
default public void attributeReplaced(ServletContextAttributeEvent event) {}

事件类,用于通知有关 Web 应用程序 ServletContext 更改的信息。

ServletContextAttributeEvent

public ServletContextEvent(ServletContext source) {
  super(source);
}
public ServletContext getServletContext () {
  return (ServletContext) super.getSource();
}

扩展了 ServletContextEvent 类,用于通知有关 Web 应用程序 ServletContext 属性更改的信息。

ServletRequest

ServletRequest 接口定义了很多 Servlet 接收客户端请求信息的方法。

ServletRequestWrapper 类实现了 ServletRequest 接口的方法。

HttpServletRequest 接口继承了 ServletRequest 接口,扩展了 Servlet 通过 Http 接收请求信息的方法。

HttpServletRequestWrapper 类继承 ServletRequestWrapper 类的基础上,又实现了 HttpServletRequest 接口方法。

ServletResponse

与 ServletRequest 接口对应,定义了返回请求相应结果的方法。

ServletResponseWrapper 类实现了 ServletResponse 接口的方法。

HttpServletResponse 接口继承了 ServletResponse 接口,扩展了 Servlet 通过 Http 返回请求相应结果的方法。

HttpServletResponseWrapper 类继承 ServletResponseWrapper 类的基础上,又实现了 HttpServletResponse 接口方法。

Filter

用于资源请求或资源响应的过滤。

private String name;
private Object value;

public ServletContextAttributeEvent(ServletContext source, String name, Object value) {
  super(source);
  this.name = name;
  this.value = value;
}

public String getName() {
  return this.name;
}

public Object getValue() {
  return this.value;
}

只提供了两个实现类。分别是直接实现 ServletConfig 接口的 GenericServlet 类,以及继承了 GenericServlet 类的 HttpServlet 类。依照类的命名 GenericServlet 是通用的协议无关的接口方法实现,而 HttpServlet 继承通用、协议无关的基础上,扩展了 Http 协议相关的方法。

总结

最后,还有部分内容涵盖。例如 Cookie、Session、Async 异步请求处理、Part 文件上传API等比较简单的内容。

最后,希望全球疫情早日结束,世界早日恢复秩序。

本文分享自微信公众号 - FoamValue(gh_3c635269f459),作者:FoamValue

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-03-31

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 结构型构建模式

    适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式,它们是关注类和对象的组合为目的的设计模式,它们也被称为结构型构建模式。

    FoamValue
  • 创建型构建模式

    单例模式、原型模式、工程模式、建造者模式,它们是以创建对象为目的的设计模式,它们也被称为创建型构建模式。

    FoamValue
  • 「拥抱开源」从零开始 Docker、Mysql & JPA

    为了降低项目成本,我们有一套完整的 MySQL 开源社区版集群。希望能在低成本的基础上,带来期望的收益。

    FoamValue
  • 设计模式----状态模式

    SuperHeroes
  • 第十一天 面向对象-接口多态【悟空教程】

    Java帮帮
  • Tomcat的ROOT文件夹资源文件访问不到的一个小问题

    应用上线,需要部署在Tomcat的ROOT里面,启动以后发现所有的存放用户上传的图片资源在页面上都是破的,新上传也不行。

    LeoXu
  • JAVA 设计模式 适配器模式

    用途 适配器模式 (Adapter) 将一个类的接口转换成客户希望的另外一个接口。 Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工...

    静默虚空
  • RxJava +Retrofit 你需要掌握的几个实用技巧

    RxJava +Retrofit 你需要掌握的几个技巧,Retrofit中OkHttp缓存,统一对有无网络处理, 异常处理,返回结果问题.

    开发者技术前线
  • PHP 面向对象 接口

    使用接口 interface,可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。 要实现一个接口,使用 implements 操作符。 接口中定义...

    康怀帅
  • Android使用RecycleView实现拖拽交换item位置

    本文实例为大家分享了RecycleView实现拖拽交换item位置的具体代码,供大家参考,具体内容如下

    砸漏

扫码关注云+社区

领取腾讯云代金券