Servlet系列

Servlet 系列

相关问题:

  1. Web 技术与 Servlet技术
  2. Servlet是什么
  3. Servlet和Servlet容器
  4. Servlet技术特点
  5. Servlet生命周期
  6. Servlet代码结构

Web技术发展

静态页面阶段

  1. 用户用浏览器通过 HTTP 协议请求服务器上的 Web 页面
  2. 服务器上的 Web 服务器软件接收到请求后,读取 URI 所标识的资源
  3. 在读取的资源上加上消息报头发送给客户端的浏览器。
  4. 浏览器解析响应中的 HTML 数据,想用户呈现内容

CGI 动态页面阶段

  1. 用户通过点击链接或者直接输入URL 访问 CGI 程序。
  2. Web 服务器接收到请求后,于是启动并运行 CGI 程序,由这个程序来处理用户的请求。
  3. CGI 程序解析请求中的 CGI 数据,处理数据,产生一个响应(通常是 HTML 页面)。
  4. 响应返回给 Web 服务器。
  5. Web 服务器包装这个响应,以 HTTP 响应的形式发送给浏览器。

Servlet 动态页面阶段

  1. 用户通过点击链接或者直接输入 URL 访问 Servlet。
  2. Web 服务器接收到请求后,交给 Servlet 容器。
  3. Servlet 容器实例化 Servlet。
  4. 调用 Servlet 特定方法对请求进行处理,并且产生一个响应。
  5. 响应由 Servlet 容器返回给 Web 容器
  6. Web 容器包装这个响应,以 HTTP 响应的形式发送给浏览器。

参考:https://blog.csdn.net/qq_34594236/article/details/78334712

Servlet 是什么

Servlet 是一种 独立于平台和协议服务器端的 Java 技术,可以用来生成动态的 Web 页面。

Servlet 是使用 Java Servlet 应用程序设计接口(API)及相关类和方法的 Java 程序

Servlet 主要用户处理客户端传来的 HTTP 请求,并返回一个响应。通常 Servlet 是指 HttpServlet,用于处理 HTTP 请求。

能够处理的请求有:doGet() doPost() service() 等。

在开发 Servlet 时,可以直接继承 javax.servlet.http.HttpServlet

Servlet 与 Servlet 容器 ⭐

ServletJava 服务器小程序)是一个基于Java 的 Web 组件,运行在服务器端,由 Servlet 容器所管理,用于生成动态的内容。

Servlet 是平台独立的 Java 类,编写一个Servlet,实际上就是按照 Servlet 规范编写一个 Java 类。Servlet 被编译为平台独立的字节码,可以被动态地加载到支持 Java 技术的 Web 服务器中运行。

Servlet 容器 (Servlet 引擎)是 Web 服务器或应用程序服务器的一部分,用于在发送的请求和响应之上提供网络服务,解码基于 MIME 的请求,格式化机遇 MIME 的响应。

区别 Servlet 不能独立于运行,必须被部署到 Servlet 容器中,由容器来实例化和调用 Servlet 的方法,Servlet 容器在Servlet 的生命周期内保存和管理 Servlet。

1. 自定义的 Servlet 运行流程

url-pattern ——> servlet-name ——> servlet-class ——> servlet.service()

Image
  1. 浏览器输入 url,先查看是否有对应的 静态资源,如果没有则尝试请求动态资源
  2. 匹配 servlet 的 url-pattern
  3. 根据 url-pattern 找到 servlet-name
  4. 根据 servlet-name 找到 servlet-class
  5. 找到对应的 servlet,调用 service() 方法进行处理

2. Servlet 生命周期

Servlet 的生命周期:在一个 http 请求到达服务器,访问 Servlet 容器时,如果已存在 Servlet 服务,则调用 Service 服务。如果没有,则进行实例化,然后执行 init() 方法,然后再调用 Service() 服务,使用其中的 get() 方法或 post() 方法,到进程需要销毁时,调用 destroy() 方法,服务器关闭时自动调用 destroy 方法进行销毁。

总体来说,生命周期如下:

  1. 加载并实例化

  2. init()

  3. service()

  4. destroy()

Servlet 生命周期 —— 默认懒加载

  1. 第一次访问创建servlet。创建实例,init() 初始化、service() 处理请求

  2. 第 234... 次访问 service() 处理请求

  3. 关闭服务器,销毁 destory()

参考:

  1. 什么是Servlet容器?https://blog.csdn.net/fg881218/article/details/89716366

3. GenericServlet

采用适配器模式

提供一个抽象类GenericServlet【标准通用的Servlet】,该类作为Servlet接口的适配器,以后编写Servlet类不再直接实现Servlet接口了,继承GenericServlet即可。重点实现service方法。

/**
 * Defines a generic, protocol-independent servlet. To write an HTTP servlet for
 * use on the Web, extend {@link javax.servlet.http.HttpServlet} instead.
 * <p>
 * <code>GenericServlet</code> implements the <code>Servlet</code> and
 * <code>ServletConfig</code> interfaces. <code>GenericServlet</code> may be
 * directly extended by a servlet, although it's more common to extend a
 * protocol-specific subclass such as <code>HttpServlet</code>.
 * <p>
 * <code>GenericServlet</code> makes writing servlets easier. It provides simple
 * versions of the lifecycle methods <code>init</code> and <code>destroy</code>
 * and of the methods in the <code>ServletConfig</code> interface.
 * <code>GenericServlet</code> also implements the <code>log</code> method,
 * declared in the <code>ServletContext</code> interface.
 * <p>
 * To write a generic servlet, you need only override the abstract
 * <code>service</code> method.
 */
public abstract class GenericServlet implements Servlet, ServletConfig,
        java.io.Serializable {

    private static final long serialVersionUID = 1L;

    private transient ServletConfig config;

    public GenericServlet() {
        // NOOP
    }

    @Override
    public void destroy() {
        // NOOP by default
    }

    @Override
    public String getInitParameter(String name) {
        return getServletConfig().getInitParameter(name);
    }

    @Override
    public Enumeration<String> getInitParameterNames() {
        return getServletConfig().getInitParameterNames();
    }

    @Override
    public ServletConfig getServletConfig() {
        return config;
    }

    @Override
    public ServletContext getServletContext() {
        return getServletConfig().getServletContext();
    }

    @Override
    public String getServletInfo() {
        return "";
    }

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

    public void init() throws ServletException {
        // NOOP by default
    }

    public void log(String msg) {
        getServletContext().log(getServletName() + ": " + msg);
    }

    public void log(String message, Throwable t) {
        getServletContext().log(getServletName() + ": " + message, t);
    }

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


    @Override
    public String getServletName() {
        return config.getServletName();
    }
}

参考:https://blog.csdn.net/cheng_feng_xiao_zhan/article/details/98073961

4. HttpServlet

HttpServlet 重载了 service 方法,并在其中定义了 模板方法

模板方法:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

如果创建 HttpServlet 子类并重写 service(HttpServletRequest req, HttpServletResponse resp) 方法,就失去了 模板方法的意义。

//继承的 service 方法
@Override
public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException {

    HttpServletRequest  request;
    HttpServletResponse response;

    try {
        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;
    } catch (ClassCastException e) {
        throw new ServletException("non-HTTP request or response");
    }
    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;
            try {
                ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            } catch (IllegalArgumentException iae) {
                // Invalid date header - proceed as if none was set
                ifModifiedSince = -1;
            }
            if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                // 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);

    }
    ....
}

// get 模板
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(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
    } else {
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
    }
}

// post 模板
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(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
    } else {
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
    }
}

参考:

  1. 模板方法模式 https://www.runoob.com/design-pattern/template-pattern.html

  2. 继承 HttpServlet 类—模板方法设计模式 https://blog.csdn.net/u011541946/article/details/89954522

5. Servlet 特点

高效,方便,跨平台,功能强大,灵活性和可扩展性,共享数据,安全。

  1. 高效
    在服务器上仅有一个 Java 虚拟机在运行,它的优势在于当多个来自客户端的请求进行访问时,Servlet 为每个请求分配一个线程而不是进程。

  2. 方便
    Servlet 提供了大量的实用工具例程,如处理很难完成的 HTML 表单数据,读取和设置 HTTP 头,处理 Cookie 和跟踪会话等。

  3. 跨平台
    Servlet 是用 Java 类编写的,可以在不同的操作系统平台和应用服务器平台下运行。

  4. 功能强大
    在 Servlet 中,许多实用传统 CGI 程序很难完成的任务都可以利用 Servlet 技术轻松完成。例如,Servlet 能够直接和 Web 服务器交互,而普通的 CGI 程序不能。Servlet 还能够在各个程序之间共享数据,使得数据库连接池之类的功能很容易实现。

  5. 灵活性和可扩展性
    采用 Servlet 开发的 Web 应用程序,由于 Java 类的继承性,构造函数等特点,使得其应用灵活,可随意扩展。

  6. 共享数据
    Servlet 之间通过共享数据可以很容易地实现数据库连接池。它能方便地实现管理用户请求,简化 Session 和获取前一页面信息的操作,而在 CGI 之间通信则很差。由于每个 CGI 程序的调用都开始一个新的进程,调用间通信通常要通过文件进行,因而相当缓慢。同一台服务器上的不同 CGI 程序之间的通信也相当麻烦。

  7. 安全
    有些 CGI 版本有明显的安全弱点。即使是实用最新标准,系统也没有基本安全框架。而 Java 定义有完整的安全机制,包括 SSL\CA 认证、安全政策等规范。

6. Servlet 与 CGI 的区别?

CGI:Common Gateway Interface 公共网关接口

与传统的 CGI 和许多其他类似 CGI 的技术相比,Java Servlet 具有更高的效率,更容易使用功能更强大,具有更好的可移植性更节省投资。在未来的技术发展过程中,Servlet 有可能彻底取代 CGI。

开销低,节省资源 在传统的 CGI 中,每个请求都要启动一个新的进程,如果 CGI 程序本身的执行时间较短,启动进程所需要的开销很可能反而超过实际执行时间。而在 Servlet 中,每个请求由一个轻量级的 Java 线程处理(而不是重量级的操作系统进程)。

在传统 CGI 中,如果有 N 个并发的对同一 CGI 程序的请求,则该 CGI 程序的代码在内存中重复装载了 N 次;而对于 Servlet,处理请求的是 N 个线程,只需要一份 Servlet 类代码。在性能优化方面,Servlet 也比 CGI 有着更多的选择。

方便,容易使用 Servlet 提供了大量的实用工具,例如自动地解析和解码 HTML 表单数据、读取和设置 HTTP 头、处理Cookie、跟踪会话状态等。

功能强大 在 Servlet 中,许多使用传统 CGI 程序很难完成的任务都可以轻松地完成。例如,Servlet 能够直接和 WEB 服务器交互,而普通的 CGI 程序不能。Servlet 还能够在各个程序之间共享数据,使得 数据库连接池之类的功能很容易实现。

可移植性好 Servlet 用 Java 编写,Servlet API 具有完善的标准。因此,为 IPlanet Enterprise Server 写的 Servlet 无需任何实质上的改动即可移植到 Apache、Microsoft IIS 或者 WebStar。几乎所有的主流服务器都直接或通过插件支持 Servlet。

参考:servlet 和 cgi 的区别和联系

  1. https://www.cnblogs.com/MuyouSome/p/3938203.html
  2. https://blog.csdn.net/lixisd/article/details/81604024
posted @ 2021-09-28 12:47  egu0o  阅读(29)  评论(0编辑  收藏  举报