Java Sevlet 技术

Servlet是什么?

    Servlet是运行于服务器端,使用Java Servlet API 以及相关类和方法实现的Java程序。

为什么说Servlet独立于平台?

    Java Servlet API 定义了一个 Servlet和Java使能服务器(Servlet 容器)之间的一个标准接口,这使得Servlet具有跨平台的特性。

为什么说Servlet与协议无关?

    Servlet不对具体的协议实现,可以接受自定义协议,常用的WEB项目HttpServlet 是对HTTP协议的实现,我们可以像HttpServlet一样扩展GenericServlet 来实现FtpServlet,TelnetServlet等等。

为什么说Servlet相对高效?

    Servlet在第一次被请求加载时调用init方法初始化一个Servlet,当后续的客户请求 servlet 服务时,Web 服务都将启动一个新的线程,而不是启动一个进程,这些线程由Servlet引擎服务器来管理,与传统的 CGI 为每个客户启动一个进程相比较,效率要高的多。

Servlet的生命周期

    Servlet 的生命周期始于将它装入 Web 服务器的内存时,并在终止或重新装入 Servlet 时结束。Servlet的生命周期大致分三个阶段:

1. 初始化。

    装入 Servlet 后,服务器创建一个 Servlet 实例并且调用 Servlet 的 init() 方法。在初始化阶段,Servlet 初始化参数(ServletConfig)传递给 Servlet 配置对象。 初始化的三个时机:

    1.1 如果已配置自动装入选项,则在启动服务器时自动装入。

    1.2 在服务器启动后,客户机首次向 Servlet 发出请求时。

    1.3 重新装入Servlet时。

2. 请求处理

    对于到达服务器的客户机请求,服务器创建特定于请求的一个“请求”对象和一个“响应”对象。服务器调用 Servlet 的 service() 方法,该方法用于传递“请求”和“响应”对象。service() 方法从“请求”对象获得请求信息、处理该请求并用“响应”对象的方法以将响应传回客户机。service() 方法可以调用其它方法来处理请求,例如 doGet()、doPost() 或其它的方法。 

    例如对于Http请求:

//HttpServlet中service方法
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);//看下方service()方法代码
 }

  HttpServlet中service()方法获得请求信息并响应传回客户机 源码:

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 / 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);        
            
        } 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);
        }
}

3. 终止Servlet

    当服务器不再需要 Servlet, 或重新装入 Servlet 的新实例时,服务器会调用 Servlet 的 destroy() 方法。 

Java Servlet API简介

    Java Servlet 开发工具(JSDK)提供了多个软件包,在编写 Servlet 时需要用到这些软件包。其中包括两个用于所有 Servlet 的基本软件包:javax.servlet 和 javax.servlet.http。javax.servlet包中的类都是抽象的,比较高层的,与协议无关的。下面主要介绍javax.servlet.http提供的HTTP Servlet应用编程接口。

    HTTP Servlet 使用一个 HTML 表格来发送和接收数据。要创建一个 HTTP Servlet,需扩展 HttpServlet 类, 该类是用专门的方法来处理 HTML 表格的 GenericServlet 的一个子类。 HttpServlet 类包含 init()、destroy()、service() 等方法。其中 init() 和 destroy() 方法是继承的。

    相关类图:

    下面重点说几个常用且重要的方法:

1. init()方法

    在 Servlet 的生命期中,仅执行一次 init() 方法。它是在服务器装入 Servlet 时执行的。 可以配置服务器,以在启动服务器或客户机首次访问 Servlet 时装入 Servlet。 无论有多少客户机访问 Servlet,都不会重复执行 init() 。

2. service()方法

    service() 方法是 Servlet 的核心。每当一个客户请求一个HttpServlet 对象,该对象的service() 方法就要被调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。

3. destroy() 方法
  destroy() 方法仅执行一次,即在服务器停止且卸装Servlet 时执行该方法。典型的,将 Servlet 作为服务器进程的一部分来关闭。缺省的 destroy() 方法通常是符合要求的,但也可以覆盖它,典型的是管理服务器端资源。

Servlet线程安全性问题

    Servlet是非线程安全的。Servlet的线程安全问题主要是由于实例变量使用不当而引起。

    Servlet体系结构是建立在Java多线程机制之上的,它的生命周期是由Web容器负责的。当客户端第一次请求某个Servlet时,Servlet容器将会根据web.xml配置文件实例化这个Servlet类。当有新的客户端请求该Servlet时,一般不会再实例化该Servlet类,也就是有多个线程在使用这个实例。Servlet容器会自动使用线程池等技术来支持系统的运行。

    这样,当两个或多个线程同时访问同一个Servlet时,可能会发生多个线程同时访问同一资源的情况,数据可能会变得不一致。

    线程安全问题主要是由实例变量造成的,因此:

    1. 在Servlet中应避免使用实例变量。

    2. 如果应用程序设计无法避免使用实例变量,那么使用同步来保护要使用的实例变量,但为保证系统的最佳性能,应该同步可用性最小的代码路径。

原文地址:https://www.cnblogs.com/shitouer/p/2207402.html