0


Servlet生命周期

引言

编写一个HTTP服务器,要先编写基于TCP协议的服务,然后在一个TCP连接中读取HTTP请求,发送HTTP响应即可。要编写一个完善的

HTTP

服务器,以

HTTP/1.1

为例,需要考虑的包括:

  • 识别正确和错误的HTTP请求;
  • 识别正确和错误的HTTP头;
  • 复用TCP连接;
  • 复用线程;
  • IO异常处理;

这些基础工作需要耗费大量的时间,并且经过长期测试才能稳定运行。如果我们只需要输出一个简单的

HTML

页面,就不得不编写上千行底层代码,那就根本无法做到高效而可靠地开发。

Servlet

JavaEE

平台上,处理

TCP

连接,解析

HTTP

协议这些底层工作统统扔给现成的

Web

服务器去做,我们只需要把自己的应用程序跑在

Web

服务器上。为了实现这一目的,

JavaEE

提供了

Servlet 
API

,我们使用

Servlet API

编写自己的

Servlet

来处理

HTTP

请求,

Web

服务器实现

Servlet 
API

接口,实现底层功能:

                 ┌───────────┐
                 │My Servlet │
                 ├───────────┤
                 │Servlet API│
┌───────┐  HTTP  ├───────────┤
│Browser│<──────>│Web Server │
└───────┘        └───────────┘

简单来讲,一个Web App就是由一个或多个

Servlet

组成的,每个

Servlet

通过注解说明自己能处理的路径。一个

Webapp

完全可以有多个

Servlet

,分别映射不同的路径。例如:

HelloServlet

能处理

/hello.do

这个路径的请求。

**一个简单的Servlet: **

// WebServlet注解表示这是一个Servlet,并映射到地址 hello.do
@WebServlet(urlPatterns = "/hello.do")
public class HelloServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // 设置响应类型:
        resp.setContentType("text/html");
        // 获取输出流:
        PrintWriter pw = resp.getWriter();
        // 写入响应:
        pw.write("<h1>Hello, world!</h1>");
        // 最后使用flush强制输出:
        pw.flush();
    }
}

在浏览器输入

http://localhost:8080/项目名称/hello.do

,即可看到

HelloServlet

的输出:

一个

Servlet

总是继承自

HttpServlet

,然后覆写

doGet()

doPost()

方法。注意到

doGet()

方法传入了

HttpServletRequest

HttpServletResponse

两个对象,分别代表

HTTP

请求和响应。我们使用

Servlet API

时,并不直接与底层

TCP

交互,也不需要解析

HTTP

协议,因为

HttpServletRequest

HttpServletResponse

就已经封装好了请求和响应。以发送响应为例,我们只需要设置正确的响应类型,然后获取

PrintWriter

,写入响应即可。

要想运行Servlet,就要使用支持

Servlet
API

的Web容器(Web服务器)。常用的服务器有:

  • Tomcat:由Apache开发的开源免费服务器;
  • Jetty:由Eclipse开发的开源免费服务器;
  • GlassFish:一个开源的全功能JavaEE服务器。

还有一些收费的商用服务器,如Oracle的WebLogic,IBM的WebSphere。

**在

Servlet

容器中运行的

Servlet

具有如下特点**:

  • 无法在代码中直接通过new创建Servlet实例,必须由Servlet容器自动创建Servlet实例;
  • Servlet容器只会给每个Servlet类创建唯一实例;
  • Servlet容器会使用多线程执行doGet()doPost()方法。

Servlet生命周期

在通过一个

URL

路径发起对一个

Servlet

请求的过程中,其本质是在调用执行

Servlet

实例的

doXXX()

方法。该

Servlet

实例创建和使用的过程,被称为Servlet的生命周期。整个生命周期包括:实例化、初始化、服务、销毁。

实例化

根据

Servlet

请求的路径(例如:

home.do

),查找该

Servlet

实例。如果实例不存在,则通过调用构造方法,完成

Servlet

实例的创建。


//WebServlet注解表示这是一个Servlet,并映射到地址 hello.do
@WebServlet(urlPatterns = "/hello.do")
public class HelloServlet extends HttpServlet {
    //记录客户端(服务器)发起请求的次数
    int count ;
    //定义其无参构造方法
    public HelloServlet(){
        System.out.println("Servlect实例化开始,对象被创建!");
    }
 protected void doGet(HttpServletRequest req, HttpServletResponse resp)
         throws ServletException, IOException {
     System.out.println("收到客户端的请求:"+(++count)+"次");
     PrintWriter pw = resp.getWriter();
     // 写入响应:
     pw.write("<h1>Hello, world!</h1>");
     pw.flush();
 }
}

客户端(服务器)第一次发起请求时Tomcat没有找到Servlet实例,所以就调用构造方法,完成对Servlect的实例化,当再次客户端再次发起请求时,由于Servlet对象已经存在,故不再调用构造方法!

初始化

通过该

Servlet

实例,调用

init()

方法,执行初始化的逻辑。

还是上面HelloServlet类中的代码,在此类中定义无参的构造方法并且重写init()方法,当有客户端发起请求,运行结果如下:

服务

通过该

Servlet

实例,调用

service()

方法,如果子类没有重写该方法,则调用HttpServlet父类的

service()

方法,在父类的该方法中进行请求方式的判断,如果是

GET

请求,则调用

doGet()

方法;如果是

POST

请求,则调用

doPost()

方法;如果子类重写

doXXX()

方法,则调用子类重写后的

doXXX()

方法;如果子类没有重写

doXXX()

方法,则调用父类的

doXXX()

方法,在父类的方法实现中,返回一个

405

状态码的错误页面。

源代码:

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

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

如果我们在HelloServlet类中简单重写service()方法,那么当有客户端发起请求,服务器执行service()方法,就不会做出响应。

** 运行结果:**

销毁

服务器关闭或重启时,会调用Servlet实例的

destroy()

方法,会销毁所有的Servlet实例。

从一个客户端(浏览器)发起请求,到服务器做出响应的过程如图:

标签: servlet java 服务器

本文转载自: https://blog.csdn.net/m0_59340907/article/details/126442765
版权归原作者 磨剑斩秋招 所有, 如有侵权,请联系我们删除。

“Servlet生命周期”的评论:

还没有评论