Servlet运行原理以及生命周期

时间:2022-02-11 18:08:10

一、Servlet生命周期:

Servlet加载、实例化、初始化、服务、销毁。

Servlet运行原理以及生命周期

1、初始化init():

当服务启动时,Servlet被装入tomcat或者其他服务器容器时执行(服务器容器从启动到停止期间唯一的一次)init()初始化方法,负责初始化Servlet对象,无论有多少个请求访问Servlet,初始化init()只会启动一次,不会重复执行。

Servlet运行原理以及生命周期

2、服务service:

服务service方法是Servlet核心,该方法有两个参数HttpServletRequest arg0, HttpServletResponse arg1:

Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。
request和response对象即然代表请求和响应,那我们要获取客户机提交过来的数据,只需要找request对象就行了。要向客户机输出数据,只需要找response对象就行了。

HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的所有信息。

HttpServletResponse对象代表服务器的响应。这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。查看HttpServletResponse的API,可以看到这些相关的方法。

在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能(这种doXxx的方法也叫钩子方法如doGet,doPost)。

如果在继承HttpServlet的类中对Serlvet进行重写,那么客户端请求Serlvet的时候,就会

去子类中调用重写的service方法(一般不对Serlvet方法进行重写)

Servlet运行原理以及生命周期

3、销毁destroy():

Serlvet在tomcat等服务器停止运行且卸载它时调用销毁destroy()方法,因为service方法运行时有可能有多个线程,当执行了销毁destroy()方法,这些线程就全部终止。

Servlet运行原理以及生命周期

二、ServletTomcat等服务器关系:

  1. web客户端 向Servlet容器(Tomcat)发出Http请求
  2. Servlet容器接收web客户端的请求
  3. Servlet容器创建一个HttpRequest对象,将Web
    Client请求的信息封装到这个对象中。
  4. Servlet容器创建一个HttpResponse对象
  5. Servlet容器调用HttpServlet对象的service方法,把HttpRequest对象与HttpResponse对象作为参数传给 HttpServlet 对象。
  6. HttpServlet调用HttpRequest对象的有关方法,获取Http请求信息。
  7. HttpServlet调用HttpResponse对象的有关方法,生成响应数据。
  8. Servlet容器把HttpServlet的响应结果传给Web
    Client。

Servlet运行原理以及生命周期

三、Servlet工作原理:

1、当web客户端发送一个Http请求,Servlet创建HttpRequest对象和HttpResponse对象,将web客户端请求的信息封装到HttpRequest对象中,Servlet将HttpRequest对象和HttpResponse对象作为参数传递给HttpServlet中的service()方法,通过HttpRequest中封装的Http请求信息获取请求方式对请求进行响应的,选择调用doGet,doPost等这些钩子方法,然后再进入对应的方法中调用逻辑层的方法,实现对客户的响应。在Servlet接口和GenericServlet中是没有doGet()、doPost()等等这些钩子方法的,HttpServlet中定义了这些方法,但是都是返回error信息,所以,我们每次定义一个Servlet的时候,都必须实现doGet或doPost等这些方法。

如果服务器启动时就创建Servlet,那么还需要在web.xml文件中配置<load-on-startup>1</load-on-startup>。Servlet是在第一次被访问时由服务器创建的,而且一个Servlet类型,服务器只创建一个实例对象,它是单例的。

2、每一个自定义的Servlet都必须实现Servlet的接口,Servlet接口中定义了五个方法,其中比较重要的三个方法涉及到Servlet的生命周期,分别是上文提到的init(),service(),destroy()方法。GenericServlet是一个通用的,不特定于任何协议的Servlet,它实现了Servlet接口。而HttpServlet继承于GenericServlet,因此HttpServlet也实现了Servlet接口。所以我们定义Servlet的时候只需要继承HttpServlet即可。

服务器接收到一次请求,就会调用service() 方法一次。

3、Servlet接口和GenericServlet是不特定于任何协议的,而HttpServlet是特定于HTTP协议的类,所以HttpServlet中实现了service()方法,并将请求ServletRequest、ServletResponse 强转为HttpRequest 和 HttpResponse。

四、创建Servlet对象

1、Servlet容器启动时:读取web.xml配置文件中的信息,构造指定的Servlet对象,创建ServletConfig对象,同时将ServletConfig对象作为参数来调用Servlet对象的init方法。

2、在Servlet容器启动后:客户首次向Servlet发出请求,Servlet容器会判断内存中是否存在指定的Servlet对象,如果没有则创建它,然后根据客户的请求创建HttpRequest、HttpResponse对象,从而调用Servlet 对象的service方法。

3、Servlet Servlet容器在启动时自动创建Servlet,这是由在web.xml文件中为Servlet设置的<load-on-startup>属性决定的。从中我们也能看到同一个类型的Servlet对象在Servlet容器中以单例的形式存在。

Servlet运行原理以及生命周期

三、Servlet概述
Servlet是JavaWeb的三大组件之一,它属于动态资源。都会在web.xml中配置,例如如上图
Servlet的作用是处理请求,服务器会把接收到的请求交给Servlet来处理,在Servlet中通常需要:
    Tomcat服务器会帮助我们将请求的数据封装在request对象
    Tomcat同样会创建respose对象给我们使用,让我们响应 HttpServletRequest extends ServletRequest
  例如客户端发出登录请求,或者输出注册请求,这些请求都应该由Servlet来完成处理!
    我们需要实现自己的逻辑业务就需要自己来编写Servlet,每个Servlet必须实现javax.servlet.Servlet接口。

实现Servlet有三种方式:
1.实现javax.servlet.Servlet接口;
2.继承javax.servlet.GenericServlet类;
3.继承javax.servlet.http.HttpServlet类;

  开发中通常我们会去继承HttpServlet类来完成我们的Servlet,生命周期方法由Tomcat来调用,我们只是完成接受请求,然后响应,完成业务逻辑代码
    Tomcat服务器帮助我们完成不变的代码,我们来完成业务需求变化的代码
    我们的代码和Tomcat代码合起来构成一个完成的代码

四、补充:

近期发现对:

1、为什么不建议重写service方法?

2、servlet中为什么一定要重写doGet以及doPost方法?

3、servlet的父类;

有了新的认识,进行补充;

1、为什么不建议重写service方法?

因为客户端每一次发送请求,服务端都会调用一次servlet中service方法,而service方法其实是类似调度的功能,

service方法源码:

protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String method = req.getMethod();
        if(method.equals("GET"))
        {
            long lastModified = getLastModified(req);
            if(lastModified == -1L)
            {
                doGet(req, resp);
            } else
            {
                long ifModifiedSince = req.getDateHeader("If-Modified-Since");
                if(ifModifiedSince < (lastModified / 1000L) * 1000L)
                {
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else
                {
                    resp.setStatus(304);
                }
            }
        } else
        if(method.equals("HEAD"))
        {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);
        } else
        if(method.equals("POST"))
            doPost(req, resp);
        else
        if(method.equals("PUT"))
            doPut(req, resp);
        else
        if(method.equals("DELETE"))
            doDelete(req, resp);
        else
        if(method.equals("OPTIONS"))
            doOptions(req, resp);
        else
        if(method.equals("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(501, errMsg);
        }
    }

可以看出来,如果我们重写了service方法,没有实现对doGet和doPost等doxxx方法的调用,那么请求就得不到处理。

如果不对service方法重写,那么会执行HttpServlet方法中service方法。

2、servlet中为什么一定要重写doGet以及doPost方法?

因为在service方法对请求的方式进行了匹配,如Get方法就调用doGet方法,所以一定要根据业务逻辑对doGet或者doPost等doxxx方法进行重写

3、servlet的父类

GenericServlet是Servlet接口的实现类,我们可以通过继承GenericServlet来编写自己的Servlet。

GenericServlet还实现了ServletConfig接口,所以可以直接调用getInitParameter()、getServletContext()等ServletConfig的方法。

具体源码可以反编译去查看:

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

GenericServlet的init()方法
在GenericServlet中,定义了一个ServletConfig config实例变量,并在init(ServletConfig)方法中把参数ServletConfig赋给了实例变量。然后在该类的很多方法中使用了实例变量config。
如果子类覆盖了GenericServlet的init(StringConfig)方法,那么this.config=config这一条语句就会被覆盖了,也就是说GenericServlet的实例变量config的值为null,那么所有依赖config的方法都不能使用了。如果真的希望完成一些初始化操作,那么去覆盖GenericServlet提供的init()方法,它是没有参数的init()方法,它会在init(ServletConfig)方法中被调用。

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

HttpServlet类是GenericServlet的子类,它提供了对HTTP请求的特殊支持,所以通常我们都会通过继承HttpServlet来完成自定义的Servlet。

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

Servlet运行原理以及生命周期

HttpServlet覆盖了service()方法
HttpServlet类中提供了service(HttpServletRequest,HttpServletResponse)方法,这个方法是HttpServlet自己的方法,不是从Servlet继承来的。在HttpServlet的service(ServletRequest,ServletResponse)方法中会把ServletRequest和ServletResponse强转成HttpServletRequest和HttpServletResponse,然后调用service(HttpServletRequest,HttpServletResponse)方法,这说明子类可以去覆盖service(HttpServletRequest,HttpServletResponse)方法即可,这就不用自己去强转请求和响应对象了。

就是直接继承HttpServlet,然后无需进行重写,就可以实现ServletRequest和ServletResponse强转成HttpServletRequest和HttpServletResponse,因为在HttpServlet类已经实现了这一操作

Servlet运行原理以及生命周期

借鉴于https://blog.csdn.net/tanyunlong_nice/article/details/42536461