Introduction to Servlets and Servlet Containers – Servlet和Servlet容器介绍

最后修改: 2020年 12月 1日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll understand conceptually what servlets and servlet containers are and how they work.

在本教程中,我们将从概念上理解什么是Servlet和Servlet容器,以及它们是如何工作的

We’ll also see them in the context of a request, response, session objects, shared variables, and multithreading.

我们还将在请求、响应、会话对象、共享变量和多线程的背景下看到它们。

2. What Are Servlets and Their Containers

2. What Are Servlets and Their Containers

Servlets are a component of the JEE framework used for web development. They are basically Java programs that run inside the boundaries of a container. On the whole, they are responsible for accepting a request, processing it, and sending a response back. Introduction to Java servlets provides a good basic understanding of the subject.

Servlets是用于网络开发的JEE框架的一个组成部分。它们基本上是在一个容器的边界内运行的Java程序。总的来说,它们负责接受请求,处理请求,并发送响应Introduction to Java servlets提供了对该主题的良好的基本了解。

To use them, servlets need to be registered first so that a container, either JEE or Spring-based, can pick them up at start-up. In the beginning, the container instantiates a servlet by calling its init() method.

要使用它们,servlet需要首先注册,以便基于JEE或Spring的容器能够在启动时接收它们。在开始时,容器通过调用其init()方法来实例化一个servlet。

Once its initialization is complete, the servlet is ready to accept incoming requests. Subsequently, the container directs these requests for processing in the servlet’s service() method. After that, it further delegates the request to the appropriate method such as doGet() or doPost() based on the HTTP request type.

一旦其初始化完成,servlet就可以接受传入的请求。随后,容器在servlet的service()方法中引导这些请求进行处理。之后,它进一步将请求委托给适当的方法,如基于HTTP请求类型的doGet()doPost()

With destroy(), the container tears the servlet down, and it can no longer accept incoming requests. We call this cycle of init-service-destroy the lifecycle of a servlet.

通过destroy(),容器关闭了servlet,它不能再接受进入的请求。我们将这种init-service-destroy的循环称为servlet的生命周期

Now let’s look at this from the point of view of a container, such as Apache Tomcat or Jetty. At start-up, it creates an object of ServletContext. The job of the ServletContext is to function as the server or container’s memory and remember all the servlets, filters, and listeners associated with the web application, as described in its web.xml or equivalent annotations. Until we stop or terminate the container, ServletContext stays with it.

现在让我们从一个容器的角度来看这个问题,例如Apache TomcatJetty。在启动时,它创建一个ServletContext对象。ServletContext的工作是作为服务器或容器的内存,并记住与 Web 应用程序相关的所有 servlet、过滤器和监听器,如其 web.xml 或同等注释中所述。在我们停止或终止容器之前,ServletContext会一直伴随着它。

However, the servlet’s load-on-startup parameter plays an important role here. If this parameter has a value greater than zero, only then the server initializes it at start-up. If this parameter is not specified, then the servlet’s init() is called when a request hits it for the very first time.

然而,servlet的load-on-startup参数在这里发挥了重要作用。如果这个参数的值大于零,那么服务器才会在启动时初始化它。如果没有指定这个参数,那么当一个请求第一次击中它时,就会调用servlet的init()

3. Request, Response, and Session

3.请求、响应和会话

In the previous section, we talked about sending requests and receiving responses, which basically is the cornerstone of any client-server application. Now, let’s look at them in detail with respect to servlets.

在上一节中,我们谈到了发送请求和接收响应,这基本上是任何客户端-服务器应用程序的基石。现在,让我们详细看看它们与servlets的关系。

In this case, a request would be represented by HttpServletRequest and response with HttpServletResponse.

在这种情况下,请求将用HttpServletRequest表示,响应则用HttpServletResponse

Whenever a client such as a browser, or a curl command, sends in a request, the container creates a new HttpServletRequest and HttpServletResponse object. It then passes on these new objects to the servlet’s service method. Based on the HttpServletRequest‘s method attribute, this method determines which of the doXXX methods should be called.

每当一个客户端(如浏览器或 curl 命令)发送一个请求时,容器就会创建一个新的 HttpServletRequestHttpServletResponse 对象。然后,它将这些新对象传递给Servlet的service方法。根据HttpServletRequest的方法属性,这个方法决定了应该调用哪个doXXX方法。

Apart from the information about the method, the request object also carries other information such as headers, parameters, and body. Similarly, the HttpServletResponse object also carries headers, parameters, and body – we can set them up in our servlet’s doXXX method.

除了关于方法的信息外,请求对象还携带其他信息,如头文件、参数和正文。同样,HttpServletResponse对象也携带头信息、参数和正文–我们可以在我们的Servlet的doXXX方法中设置它们。

These objects are short-lived. When the client gets the response back, the server marks the request and response objects for garbage collection.

这些对象是短命的。当客户端得到响应后,服务器会将请求和响应对象标记为垃圾回收。

How would we then maintain a state between subsequent client requests or connections? HttpSession is the answer to this riddle.

那么我们如何在随后的客户端请求或连接之间保持一个状态呢?HttpSession就是这个谜题的答案。

This basically binds objects to a user session, so that information pertaining to a particular user can be persisted across multiple requests. This is generally achieved using the concept of cookies, using JSESSIONID as a unique identifier for a given session. We can specify the timeout for the session in web.xml:

这基本上是将对象与用户会话绑定在一起,因此与特定用户有关的信息可以在多个请求中持续存在。这通常是通过cookies的概念实现的,使用JSESSIONID作为一个特定会话的唯一标识。我们可以在web.xml中指定会话的超时。

<session-config>
    <session-timeout>10</session-timeout>
</session-config>

This means if our session has been idle for 10 minutes, the server will discard it. Any subsequent request would create a new session.

这意味着如果我们的会话已经闲置了10分钟,服务器会将其丢弃。任何后续的请求将创建一个新的会话。

4. How Do Servlets Share Data

4. Servlets如何分享数据

There’re various ways in which servlets can share data, based on the required scope.

根据所需的范围,servlets有多种方式可以共享数据。

As we saw in the earlier sections, different objects have different lifetimes. HttpServletRequest and HttpServletResponse objects only live between one servlet call. HttpSession lives as long as it’s active and hasn’t timed out.

正如我们在前面的章节中所看到的,不同的对象有不同的生命期。HttpServletRequestHttpServletResponse对象只在一次Servlet调用之间生存。HttpSession只要它处于活动状态并且没有超时,就会一直存在。

ServletContext‘s lifespan is the longest. It’s born with the web application and gets destroyed only when the application itself shuts down. Since servlet, filter, and listener instances are tied to the context, they also live as long as the web application is up and running.

ServletContext的寿命是最长的。它与Web应用程序一起诞生,只有在应用程序本身关闭时才会被销毁。由于servlet、过滤器和监听器实例与上下文相联系,因此只要Web应用在运行,它们就会一直存在。

Consequently, if our requirement is to share data between all servlets, let’s say if we want to count the number of visitors to our site, then we should put the variable in the ServletContext. If we need to share data within a session, then we’d save it in the session scope. A user’s name would be an example in this case.

因此,如果我们的要求是在所有的servlet之间共享数据,比方说,如果我们想计算我们网站的访问者数量,那么我们应该把变量放在ServletContext中。如果我们需要在一个会话中共享数据,那么我们就把它保存在会话范围内。在这种情况下,一个用户的名字就是一个例子。

Lastly, there’s the request scope pertaining to data for a single request, such as the request payload.

最后,是与单个请求的数据有关的请求范围,例如请求的有效载荷。

5. Handling Multithreading

5.处理多线程

Multiple HttpServletRequest objects share servlets among each other such that each request operates with its own thread of the servlet instance.

多个HttpServletRequest对象在彼此之间共享servlet,这样,每个请求都用它自己的servlet实例的线程来操作。

What that effectively means in terms of thread-safety is that we should not assign a request or session scoped data as an instance variable of the servlet.

这实际上意味着在线程安全方面,我们不应该将请求或会话范围的数据指定为servlet的实例变量

For example, let’s consider this snippet:

例如,让我们考虑这个片段。

public class ExampleThree extends HttpServlet {
    
    private String instanceMessage;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
      throws ServletException, IOException {
        String message = request.getParameter("message");
        instanceMessage = request.getParameter("message");
        request.setAttribute("text", message);
        request.setAttribute("unsafeText", instanceMessage);
        request.getRequestDispatcher("/jsp/ExampleThree.jsp").forward(request, response);
    }
}

In this case, all requests in the session share instanceMessage, whereas message is unique to a given request object. Consequently, in the case of concurrent requests, the data in instanceMessage could be inconsistent.

在这种情况下,会话中的所有请求都共享instanceMessage,而message对于特定的请求对象来说是唯一的。因此,在并发请求的情况下,instanceMessage中的数据可能是不一致的。

6. Conclusion

6.结语

In this tutorial, we looked at some concepts around servlets, their containers, and a few essential objects they revolve around. We also saw how servlets share data and how multi-threading affects them.

在本教程中,我们研究了围绕servlet的一些概念,它们的容器,以及它们围绕的一些基本对象。我们还看到了servlets如何共享数据以及多线程对它们的影响。

As always, source code is available over on GitHub.

一如既往,源代码可在GitHub上获取。