JavaWeb - Servlet (the most detailed tutorial in the whole network includes Servlet source code analysis)

preface

I feel worthy of [the most detailed tutorial of the whole network, including Servlet source code analysis]. I'll check it myself and share it with you.

JavaWeb-Servlet

Animation demonstration of Tomcat working mechanism (click the moving picture to watch it in full screen)

What is a Servlet

    Servlet (Server Applet), full name Java Servlet, without Chinese translation. It is a server-side program written in Java. Its main function is to interactively browse and modify data and generate dynamic Web content. In the narrow sense, servlet refers to an interface implemented in Java language, and in the broad sense, servlet refers to any class that implements this servlet interface. Generally, people understand servlet as The latter.
The Servlet runs in an application server that supports Java. In terms of implementation, servlets can respond to any type of request, but in most cases, servlets are only used to extend Web servers based on HTTP protocol.

Working mode of Servlet

  • The client sends a request to the server
  • The server starts and calls the Servlet, which generates the response content according to the client request and transmits it to the server
  • The server returns the response to the client

Servlet API overview

The Servlet API contains the following four Java packages:

1.javax. Servlets contain classes and interfaces that define the contract between servlets and servlet containers.

2.javax.servlet.http, which defines the relationship between HTTP Servlet and Servlet container.

3.javax.servlet.annotation, which contains annotations of servlet, filter and listener. It also defines metadata for labeled components.

4.javax.servlet.descriptor, which contains the type of configuration information that provides the programmatic login Web application.

Main types of servlets

How to use Servlet

The core of Servlet Technology is servlet, which is an interface that all servlet classes must implement directly or indirectly. When writing a servlet class that implements a servlet, implement it directly. When extending the class that implements this interface, implement it indirectly.

How servlets work

The servlet interface defines the contract between the servlet and the servlet container. The contract is: the servlet container loads the servlet class into memory, generates a servlet instance and calls its specific methods. Note, however, that there can only be one instance of each servlet type in an application.

The user request causes the Servlet container to call the Service () method of the Servlet and pass in a ServletRequest object and a ServletResponse object. Both the ServletRequest object and the ServletResponse object are encapsulated by the Servlet container (such as TomCat) and do not need to be implemented by the programmer. The programmer can directly use these two objects.
ServletRequest encapsulates the current Http request, so developers do not have to parse and manipulate the original Http data. ServletResponse represents the Http response of the current user. Programmers can easily send the response back to the user by directly operating the ServletResponse object.
For each application, the Servlet container also creates a ServletContext object. This object encapsulates the environment details of the context (application). Each application has only one ServletContext. Each Servlet object also has a ServletConfig object that encapsulates the Servlet configuration.

Methods defined in Servlet interface

Let's first take a look at what methods are defined in the Servlet interface.

public interface Servlet {
    void init(ServletConfig var1) throws ServletException;
 
    ServletConfig getServletConfig();
 
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
 
    String getServletInfo();
 
    void destroy();
}

Life cycle of Servlet

Init(), service(), destroy() is the method of Servlet life cycle. It represents the process of Servlet from "birth" to "work" and then to "death". The Servlet container (such as TomCat) will call these three methods according to the following rules:

1.init(), when the Servlet is requested for the first time, the Servlet container will start calling this method to initialize a Servlet object, but this method will not be called by the Servlet container in subsequent requests, just like people can only be "born" once. We can use the init () method to perform the corresponding initialization. When calling this method, the Servlet container will pass in a ServletConfig object to initialize the Servlet object.

2.service() method. Whenever a Servlet is requested, the Servlet container will call this method. Just like people, you need to constantly accept the boss's instructions and "work". In the first request, the Servlet container will first call the init() method to initialize a Servlet object, and then call its service() method to work. However, in subsequent requests, the Servlet container will only call the service method.

3. Destruction. When the Servlet is to be destroyed, the Servlet container will call this method. Just like people, they will die when the time comes. This happens when you uninstall the application or close the Servlet container. Generally, some cleanup code will be written in this method.

First, let's write a simple Servlet to verify its life cycle:

public class MyFirstServlrt implements Servlet {
 
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("Servlet Initializing");
    }
 
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }
 
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        //Methods for providing response to customer service
        System.out.println("Servlet Providing services");
 
    }
 
    @Override
    public String getServletInfo() {
        return null;
    }
 
    @Override
    public void destroy() {
        System.out.println("Servlet Destroying");
    }
}      

Then, configure the correct mapping relationship in xml and access the Servlet in the browser. During the first access, the console outputs the following information:

Then, we refresh the browser 3 times:

The information output from the console becomes as follows:

Next, we close the Servlet container:

The console outputs the destruction information of the Servlet, which is the complete life cycle of a Servlet.

The other two methods of Servlet

getServletInfo( ),This method returns Servlet Can return a string.

getServletConfig( ),This method will return Servlet Pass container to init( )Methodical ServletConfig Object.

ServletRequset interface

For each Http request received, the Servlet container will create a ServletRequest object and pass this object to the service () method of the Servlet. Among them, the ServletRequest object encapsulates many details about the request.

Let's take a look at the ServletRequest interface:

public interface ServletRequest {
  
 
    int getContentLength();//Returns the number of bytes of the request body
 
    String getContentType();//Returns the MIME type of the principal
 
    String getParameter(String var1);//Returns the value of the request parameter
 
}

Among them, getParameter is the most commonly used method in ServletRequest, which can be used to obtain the value of query string.

ServletResponse interface

       javax. Servlet. The ServletResponse interface represents a Servlet response. Before calling the Service() method of the Servlet, the Servlet container will first create a ServletResponse object and pass it to the Service() method as the second parameter. ServletResponse hides the complex process of sending a response to the browser.

Let's also take a look at the methods defined inside ServletResponse:

public interface ServletResponse {
    String getCharacterEncoding();
 
    String getContentType();
 
    ServletOutputStream getOutputStream() throws IOException;
 
    PrintWriter getWriter() throws IOException;
 
    void setCharacterEncoding(String var1);
 
    void setContentLength(int var1);
 
    void setContentType(String var1);
 
    void setBufferSize(int var1);
 
    int getBufferSize();
 
    void flushBuffer() throws IOException;
 
    void resetBuffer();
 
    boolean isCommitted();
 
    void reset();
 
    void setLocale(Locale var1);
 
    Locale getLocale();
}

The getWriter method returns a Java that can send text to the client io. PrintWriter object. By default, the PrintWriter object uses ISO-8859-1 encoding (this encoding will be garbled when entering Chinese).

When sending a response to the client, most of them use this object to send HTML to the client.

Another method can also be used to send data to the browser. It is getOutputStream. It can be seen from the name that this is a binary stream object, so this method is used to send binary data.

Before sending any HTML, You should first call the setContentType() method to set the content type of the response and pass in "text/html" as a parameter. This tells the browser that the content type of the response is HTML. You need to interpret the response content in HTML instead of ordinary text, or you can add "charset=UTF-8" to change the encoding method of the response to prevent Chinese garbled code.

ServletConfig interface

When the Servlet container initializes the Servlet, the Servlet container will pass in a ServletConfig object to the Servlet's init() method.

Several methods are as follows:

ServletContext object

The ServletContext object represents a Servlet application. Each Web application has only one ServletContext object. In a distributed environment where an application is deployed to multiple containers at the same time, the Web application on each Java virtual machine will have a ServletContext object.

By calling the getServletContext method in ServletConfig, you can also get ServletContext objects.

So why should there be a ServletContext object? There must be a reason for its existence, because with the ServletContext object, you can share the information accessed from all materials in the application, and you can dynamically register the Web object. The former saves the object in an internal Map in the ServletContext. Objects stored in ServletContext are called properties.

The following methods in ServletContext handle properties:

Object getAttribute(String var1);
 
Enumeration<String> getAttributeNames();
 
void setAttribute(String var1, Object var2);
 
void removeAttribute(String var1);
 

GenericServlet abstract class

We have always written servlets by implementing the Servlet interface. However, using this method, we must implement all the methods defined in the Servlet interface, even if there is nothing in some methods, and we also need to manually maintain the reference of the object ServletConfig. Therefore, it is troublesome to implement servlets in this way.

void init(ServletConfig var1) throws ServletException;

Fortunately, the emergence of GenericServlet abstract class solves this problem well. Based on the principle of making the code as concise as possible, GenericServlet implements the Servlet and ServletConfig interfaces. The following is the specific code of GenericServlet abstract class:

public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
    private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
    private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.LocalStrings");
    private transient ServletConfig config;
 
    public GenericServlet() {
    }
 
    public void destroy() {
    }
 
    public String getInitParameter(String name) {
        ServletConfig sc = this.getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
        } else {
            return sc.getInitParameter(name);
        }
    }
 
    public Enumeration<String> getInitParameterNames() {
        ServletConfig sc = this.getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
        } else {
            return sc.getInitParameterNames();
        }
    }
 
    public ServletConfig getServletConfig() {
        return this.config;
    }
 
    public ServletContext getServletContext() {
        ServletConfig sc = this.getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
        } else {
            return sc.getServletContext();
        }
    }
 
    public String getServletInfo() {
        return "";
    }
 
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }
 
    public void init() throws ServletException {
    }
 
    public void log(String msg) {
        this.getServletContext().log(this.getServletName() + ": " + msg);
    }
 
    public void log(String message, Throwable t) {
        this.getServletContext().log(this.getServletName() + ": " + message, t);
    }
 
    public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
 
    public String getServletName() {
        ServletConfig sc = this.getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
        } else {
            return sc.getServletName();
        }
    }
}

Among them, the GenericServlet abstract class has the following advantages over directly implementing the Servlet interface:

1. The default implementation is provided for all methods in the Servlet interface, so the programmer can directly change what he needs, and there is no need to implement all methods by himself.

2. Provide methods to surround the methods in the ServletConfig object.

3. Assign the ServletConfig parameter in the init() method to an internal ServletConfig reference to save the ServletConfig object. Programmers do not need to maintain the ServletConfig themselves.

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

However, we found that there is another Init() method without any parameters in the GenericServlet abstract class:

public void init() throws ServletException {
}

What is the original intention of the designer? In the first init () method with parameters, the ServletConfig object has been passed in and saved by reference, and the initialization process of the Servlet has been completed. Then why add an init () method without any parameters? Isn't this unnecessary?

Of course, it's not unnecessary. There must be a reason for its existence. We know that an abstract class cannot directly generate instances. If another class needs to inherit this abstract class, the problem of method coverage will occur, If the init () method of the GenericServlet abstract class is overwritten in the class, the programmer must manually maintain the ServletConfig object and call super.init(servletConfig) method to call the initialization method of the parent GenericServlet to save the ServletConfig object, which will bring great trouble to programmers. The second init() method without parameters provided by GenericServlet is to solve the above problems.

The init() method without parameters is called by the first init(ServletConfig servletconfig) method with parameters after the ServletConfig object is assigned to the ServletConfig reference. This means that if the programmer needs to override the initialization method of this GenericServlet, he only needs to override the init() method without parameters. At this time, The ServletConfig object is still saved by GenericServlet.

After all, by extending the GenericServlet abstract class, you don't need to override methods that have no plans to change. Therefore, the code will become more concise and the programmer's work will be much less.

However, although genericservlet is a good enhancement to Servlet, it is not often used because it is not as advanced as HttpServlet. HttpServlet is the protagonist and is widely used in real applications. Then let's take a look at the legendary HttpServlet.

javax.servlet.http package content

It makes sense that HttpServlet is more powerful than GenericServlet. HttpServlet is extended from GenericServlet abstract class. The declaration of HttpServlet abstract class is as follows:

public abstract class HttpServlet extends GenericServlet implements Serializable 

Another reason why HttpServlet is widely used is that most applications now use it in combination with HTTP. This means that we can use the features of HTTP to accomplish more and more powerful tasks. Javax. Servlet. The HTTP package is the second package in the Servlet API, which contains classes and interfaces for writing Servlet applications. Javax.servlet. Many types in HTTP override javax Type in Servlet.

HttpServlet abstract class

HttpServlet abstract class is inherited from GenericServlet abstract class. When using the HttpServlet abstract class, you also need to use the HttpServletRequest and HttpServletResponse objects representing the Servlet request and Servlet response respectively.

The HttpServletRequest interface is extended to javax servlet. ServletRequest interface and HttpServletResponse interface are extended to javax servlet. Servletresponse interface.

public interface HttpServletRequest extends ServletRequest
public interface HttpServletResponse extends ServletResponse

The HttpServlet abstract class overrides the Service() method in the GenericServlet abstract class and adds its own unique service (HttpServletRequest, HttpServletResponse) method.

Let's take a concrete look at how the HttpServlet abstract class implements its own service method:

First, let's look at how the service method is defined in the GenericServlet abstract class:

public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

We see that it is an abstract method, that is, HttpServlet needs to implement this service method by itself. We are looking at how HttpServlet covers this service method:

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
    HttpServletRequest request;
    HttpServletResponse response;
    try {
        request = (HttpServletRequest)req;
        response = (HttpServletResponse)res;
    } catch (ClassCastException var6) {
        throw new ServletException("non-HTTP request or response");
    }
 
    this.service(request, response);
}

We found that the service method in HttpServlet converts the received object of ServletRequsest type into an object of HttpServletRequest type and an object of ServletResponse type into an object of HttpServletResponse type. This forced conversion is possible because when calling the service method of the Servlet, the Servlet container will always pass in an HttpServletRequest object and an HttpServletResponse object to prepare to use HTTP. So, of course, the conversion type won't go wrong.

After the conversion, the service method passes two converted objects into another service method. Let's see how this method is implemented:

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String method = req.getMethod();
    long lastModified;
    if (method.equals("GET")) {
        lastModified = this.getLastModified(req);
        if (lastModified == -1L) {
            this.doGet(req, resp);
        } else {
            long ifModifiedSince = req.getDateHeader("If-Modified-Since");
            if (ifModifiedSince < lastModified) {
                this.maybeSetLastModified(resp, lastModified);
                this.doGet(req, resp);
            } else {
                resp.setStatus(304);
            }
        }
    } else if (method.equals("HEAD")) {
        lastModified = this.getLastModified(req);
        this.maybeSetLastModified(resp, lastModified);
        this.doHead(req, resp);
    } else if (method.equals("POST")) {
        this.doPost(req, resp);
    } else if (method.equals("PUT")) {
        this.doPut(req, resp);
    } else if (method.equals("DELETE")) {
        this.doDelete(req, resp);
    } else if (method.equals("OPTIONS")) {
        this.doOptions(req, resp);
    } else if (method.equals("TRACE")) {
        this.doTrace(req, resp);
    } else {
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[]{method};
        errMsg = MessageFormat.format(errMsg, errArgs);
        resp.sendError(501, errMsg);
    }
 
}
We found that this service The parameters of the method are HttpServletRequest Object and HttpServletResponse Object that just received the previous one service Method.

Next, let's look at how the service method works. We will find that there is still no service logic in the service method, but we are parsing the method parameters in HttpServletRequest and calling one of the following methods: doGet, doPost, dohead, doput, dotrace, doptions and doDelete. Each of the seven methods represents an Http method. doGet and doPost are the most commonly used. Therefore, if we need to implement specific service logic, we no longer need to override the service method, but just override doGet or doPost.

In short, HttpServlet has two features that GenericServlet does not have:

      1. Instead of overriding the service method, you override the doGet or doPost methods. In a few cases, the other five methods will be covered.

      2. HttpServletRequest and HttpServletResponse objects are used.

HttpServletRequest interface

HttpServletRequest represents a Servlet request in an Http environment. It extends to javax Servlet. ServletRequest interface and added several methods.

String getContextPath();//Returns the request URI part of the request context

Cookie[] getCookies();//Returns an array of cookie objects

String getHeader(String var1);//Returns the value of the specified HTTP header

String getMethod();//Returns the name of the HTTP method that generated the request

String getQueryString();//Returns the query string in the request URL

HttpSession getSession();//Returns the session object associated with this request

Request encapsulated in HttpServletRequest

Because Request represents a Request, we can obtain the Request line, Request header and Request body of the HTTP Request through this object.

For a detailed explanation of HTTP, please refer to my other blog post: Java Web - http.

Get the request line through request

Suppose the query string is username = Zhangsan & password = 123

Request method for obtaining the client: String getMethod()

Get the requested resource:

String getRequestURI()

StringBuffer getRequestURL()

String getContextPath() ---web Name of the application

String getQueryString() ---- get Submit url Parameter string after address

Get the request header through request

long getDateHeader(String name)

String getHeader(String name)

Enumeration getHeaderNames()

Enumeration getHeaders(String name)

int getIntHeader(String name)

The function of the referer header: the source of this access and the anti-theft chain

Get the request body through request
The content in the request body is the request parameter submitted through post. The format is:

username=zhangsan&password=123&hobby=football&hobby=basketball

key ---------------------- value

username [zhangsan]

password [123]

hobby [football,basketball]

Taking the above parameters as an example, obtain the request parameters through the following methods:

String getParameter(String name)

String[] getParameterValues(String name)

Enumeration getParameterNames()

Map<String,String[]> getParameterMap()

Note: the request parameters of get request mode can be obtained in the same way as the above methods.

Solution to Request garbled code problem

As we mentioned earlier, the default encoding and decoding method used in the service is ISO-8859-1 encoding, but this encoding does not support Chinese, so there will be garbled code. Therefore, we need to manually modify the encoding method to UTF-8 encoding to solve the problem of Chinese garbled code. The following are the specific details of garbled Code:

Solve the garbled code of post submission: request setCharacterEncoding(“UTF-8”);
To solve the garbled code of get submission method:
parameter = newString(parameter.getbytes("iso8859-1"),"utf-8");

HttpServletResponse interface

In the Service API, an HttpServletResponse interface is defined, which inherits from the ServletResponse interface and is specially used to encapsulate HTTP response messages. Since the HTTP request message is divided into three parts: status line, response message header and response message body, the method of sending response status code, response message header and response message body to the client is defined in the HttpServletResponse interface.

Response encapsulated in HttpServletResponse

Set Response through Response

void addCookie(Cookie var1);//Add a cookie to this response

void addHeader(String var1, String var2);//Add a response header to this request

void sendRedirect(String var1) throws IOException;//Send a response code that tells the browser to jump to the specified location

void setStatus(int var1);//Set the status code of the response line

addHeader(String name, String value)

addIntHeader(String name, int value)

addDateHeader(String name, long date)

setHeader(String name, String value)

setDateHeader(String name, long date)

setIntHeader(String name, int value)

Where, add represents addition and set represents setting

PrintWriter getWriter()

Obtain the character stream, and set the string into the response buffer through the write(String s) method of the character stream. Then Tomcat will assemble the contents in the response buffer into an Http response and return it to the browser.

ServletOutputStream getOutputStream()

Get the byte stream. write(byte[] bytes) of the byte stream can write bytes to the response buffer, and then the Tomcat server will form an Http response composed of byte contents and return it to the browser.

Note: Although the getOutSream () and getWriter () methods of the response object can send the response message body, they are mutually exclusive and cannot be used at the same time, otherwise an exception will occur.

Garbled Response

Reason: the default code of the response buffer is iso8859-1, and there is no Chinese in this code table. Therefore, you need to change the encoding method of response:

Changing the encoding mode of response to UTF-8 still can not solve the problem of garbled code, because although the transmitting server has changed the encoding mode to UTF-8, the browser of the receiving end still decodes using GB2312 encoding mode and still cannot restore normal Chinese. Therefore, it is also necessary to inform the browser to decode using UTF-8 encoding.

The above two methods are called to change the encoding method of the server for the Response and the decoding method of the browser to the same UTF-8 encoding to solve the problem of garbled code caused by different encoding methods.

     response. The setcontenttype ("text/html;charset=UTF-8") method contains calls to the above two methods, so in actual development, only one response needs to be called Setcontenttype ("text/html;charset=UTF-8") method is sufficient.


Workflow of Response

Workflow of Servlet

Write the first Servlet

First, let's write a simple html file for the login interface of user name and password:

<form action="/form" method="get">

When the html file finally clicks the submit button, it sends all the data of the form to the / form virtual path through Get:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/form" method="get">
    <span>user name</span><input type="text" name="username"><br>
    <span>password</span><input type="password" name="password"><br>
    <input type="submit" name="submit">
</form>
 
</body>
</html>

Visit the simple login interface we just wrote:

Next, we will start to write a Servlet to receive and process the requests sent by the form. The name of this Servlet is called FormServlet:

public class FormServlet extends HttpServlet {
    private static final long serialVersionUID = -4186928407001085733L;
 
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 
    }
 
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         //Set the encoding format of the response to UTF-8 encoding, otherwise Chinese garbled code will occur
        response.setContentType("text/html;charset=UTF-8");
        //1. Method of obtaining the request
        String method = request.getMethod();
        //2. Obtain the content related to the requested resources
        String requestURI = request.getRequestURI();//Get request URI
        StringBuffer requestURL = request.getRequestURL();
        String webName = request.getContextPath();//Get app path (APP name)
        String querryString = request.getQueryString();//Get query string
 
        response.getWriter().write("<h1>The following is the obtained string</h1>");
        response.getWriter().write("<h1>method(HTTP method):<h1>");
        response.getWriter().write("<h1>"+method+"</h1><br>");
        response.getWriter().write("<h1>requestURi(request URI):</h1>");
        response.getWriter().write("<h1>" + requestURI + "</h1><br>");
        response.getWriter().write("<h1>webname(apply name):</h1>");
        response.getWriter().write("<h1>" + webName + "</h1><br>");
        response.getWriter().write("<h1>querrystring(Query string):</h1>");
        response.getWriter().write("<h1>" + querryString + "</h1>");
 
 
    }
}

The function of the Servlet is to receive the HTTP request sent from the form login form, parse some parameters encapsulated in the request, write back to the response response, and finally display it on the browser.

Finally, we configure the mapping relationship of this Servlet in XML:

</servlet-mapping>
    <servlet>
        <servlet-name>FormServlet</servlet-name>
        <servlet-class>com.javaee.util.FormServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>FormServlet</servlet-name>
        <url-pattern>/form</url-pattern>
    </servlet-mapping>

Next, start tomcat and enter the address of the login form in the browser:

Fill in the user name: root, password: 123, and finally click submit:

After submitting, the form data will be sent to the corresponding Servlet for processing. At this time, the browser address will become as follows:

We will find that in the address bar, there is more "? Username = root & password = 123 & submit = submit" string in the back, which is actually the parameter we started to fill in and sent it by the Get method, so the query string will be directly added to the link. If the Post method is adopted, it will not appear in the link. Therefore, Login forms are mostly submitted by Post for security.

Let's see what Servlet returns to us:

As we wrote in the Servlet, the Servlet parses some parameters in the HTTP request.

Therefore, you can turn to the Servlet above to understand the working principle of Servlet again, and you may have a clearer understanding.

Limitations of servlets

We have seen that if the Servlet needs to return data to the client, such as an HTML file like the following:

The output statement needs to be written in the Servlet as follows:

PrintWriter writer = response.getWriter();
writer.write("<!DOCTYPE html>\n" +
        "<html>\n" +
        "\t<head>\n" +
        "\t\t<meta charset=\"UTF-8\">\n" +
        "\t\t<title>Title label</title>\n" +
        "\t</head>\n" +
        "\t<body>\n" +
        "\t\t<!--Title label-->\n" +
        "\t\t<h1>Company profile</h1><br />\n" +
        "\t\t<h2>Company profile</h2><br />\n" +
        "\t\t<h3>Company profile</h3><br />\n" +
        "\t\t<h4>Company profile</h4><br />\n" +
        "\t\t\n" +
        "\t\t<!--Add a horizontal line-->\n" +
        "\t\t<hr />\n" +
        "\t\t\n" +
        "\t\t<h5>Company profile</h5><br />\n" +
        "\t\t<h6>Company profile</h7><br />\n" +
        "\t\t<h100>Company profile</h100>\n" +
        "\t</body>\n" +
        "</html>\n");

That is, HTML statements are output by Writer line by line, and early simple web pages can cope with it. However, with the continuous development of the Internet, the content and function of the website are becoming more and more powerful. An ordinary HTML file may reach hundreds of lines. If you use Servlet to output HTML code line by line, It will be very cumbersome and waste a lot of time. At that time, PHP, a dynamic language that can be embedded into HTML files, made it extremely simple and easy to make dynamic web pages. Therefore, a large number of programmers turned to the path of PHP language, and the share of JAVA decreased sharply. At that time, Sun company, the developer of JAVA, in order to solve this problem, We have also developed our own dynamic web page generation technology, so that JAVA code can also be embedded in HTML files. This is the current JSP technology. We will leave the specific content of JSP technology to the next section.

ServletContextListener (Servlet global listener)

First of all, ServletContextListener is an interface. We can write any class. As long as this class implements the ServletContextListener interface, this class implements the function of listening to ServletContext. Then, how is this magical interface defined? Let's take a look at the internal situation of this interface:

package javax.servlet;

import java.util.EventListener;

public interface ServletContextListener extends EventListener {
    void contextInitialized(ServletContextEvent var1);

    void contextDestroyed(ServletContextEvent var1);
}

We found that only two methods are declared in this interface, namely void contextInitialized(ServletContextEvent var1) and void contextDestroyed(ServletContextEvent var1). Therefore, we can easily guess that there are only two kinds of lives of ServletContext, namely:

     1.ServletContext initialization. (when applying start) ---------- > the servlet container calls void contextInitialized(ServletContextEvent var1)

     2.ServletContext destroyed. (when applying stop) ---------- > the servlet container calls void contextDestroyed(ServletContextEvent var1)

Therefore, we can probably guess the working mechanism of the ServletContextListener. When the application starts, the ServletContext is initialized, and then the Servlet container will automatically call the void contextInitialized(ServletContextEvent var1) method of the ServletContextListener listening to the ServletContext and pass in a ServletContextEvent object. When the application stops, the ServletContext is destroyed. At this time, the Servlet container will automatically call the void contextDestroyed(ServletContextEvent var1) method of the ServletContextListener listening to the ServletContext.

To verify our guess, let's write a random class and implement the ServletContextListener interface, that is, the function of listening to ServletContext:

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("ServletContextListener.contextInitialized Method called");
    }
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("ServletContextListener.contextDestroyed Method called");
    }
}

Then, on the web Register the MyListener written by ourselves in XML:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <listener>

        <listener-class>MyListener</listener-class>

    </listener>

</web-app>

Next, let's start Tomcat and see what happens! The console prints the following information:

We found that when the application starts, ServletContextListener The contextinitialized () method was called. In fact, this is what the Servlet container does secretly. Then, when we stop tomcat, the Servlet container should also secretly call the void contextDestroyed(ServletContextEvent var1) method to notify the ServletContextListener listener that the ServletContext has been destroyed. As like as two peas, we are not sure. Let's stop Tomcat and take a look at the console:

We found that the void contextDestroyed(ServletContextEvent var1) method was indeed called by the Servlet container. Therefore, our conjecture has been confirmed.

[advanced] Application of ServletContextListener in Spring

If you have a better foundation of children's shoes or have learned the Spring framework, you are recommended to read the following content. It doesn't matter if you haven't learned Spring. You can learn it first or go back to see how the Spring container is instantiated by using the ServletContextListener interface.

First of all, let's review the concept of ServletContext. ServletContext is translated into Chinese as "Servlet context" or "Servlet global", but I think the translation is a little far fetched, which also leads many developers to not understand what this variable specifically represents. In fact, ServletContext is a "domain object". It exists in the whole application and has only one copy in the whole application. It represents the "state" of the current whole application. You can also understand that the ServletContext at a certain time represents the "snapshot" of the application at a certain time. This "snapshot" contains a lot of information about the application, All components of the application can obtain the status information of the current application from the ServletContext. ServletContext is created when the program starts and destroyed when the program stops. Generally speaking, we can "save" things in the ServletContext domain object, and then "take them out" in other places.

As we know, the Spring container can:

ApplicationContext ctx=new ClassPathXmlApplicationContext("path of configuration file");

Explicitly instantiate a Spring IOC container. It can also be on the web as follows Register the Spring IOC container in XML:

<listener>

    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

<context-param>

    <param-name>contextConfigLocation</param-name>

    <param-value>

        classpath:applicationContext.xml
    </param-value>

</context-param>

The listener class [org.springframework.web.context.ContextLoaderListener] implements the ServletContextListener interface and can monitor the "initialization" and "destruction" in the life cycle of ServletContext. Note that this [org.springframework.web.context.ContextLoaderListener] of course, we didn't write the listener class ourselves. It was written by someone else's Spring team. We just need to use it. Of course, don't forget to import the relevant jar package. (spring-web-4.2.4.RELEASE.jar)

Then, how to implement the listener class provided by the Spring team: after the ServletContext is initialized, can the Spring IOC container be initialized? With curiosity, let's take another look at the internal implementation of [org.springframework.web.context.ContextLoaderListener].

package org.springframework.web.context;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    public ContextLoaderListener() {
    }
    public ContextLoaderListener(WebApplicationContext context) {
        super(context);
    }
--------------------------------------------------------Focus on the following here!-----------------------------------------------------------------------

    public void contextInitialized(ServletContextEvent event) {
        this.initWebApplicationContext(event.getServletContext());
    }
--------------------------------------------------------Focus on the above and here!-----------------------------------------------------------------------

    public void contextDestroyed(ServletContextEvent event) {
        this.closeWebApplicationContext(event.getServletContext());
        ContextCleanupListener.cleanupAttributes(event.getServletContext());
    }
}

We found that, [org.springframework.web.context.ContextLoaderListener] this class implements two methods in the ServletContextListener interface. After the ServletContext is initialized, the public void contextinitialized (servletcontextevent) method is called, and then initWebApplicationContext(event.getServletContext()) is executed )Method, but we found that this method is not declared in this class. Therefore, let's take a look at how it is declared in its parent class:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
 
    if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
 
        throw new IllegalStateException("Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!");
 
    } else {
 
        Log logger = LogFactory.getLog(ContextLoader.class);
 
        servletContext.log("Initializing Spring root WebApplicationContext");
 
        if (logger.isInfoEnabled()) {
 
            logger.info("Root WebApplicationContext: initialization started");
 
        }
 
 
 
        long startTime = System.currentTimeMillis();
 
 
 
        try {
 
            if (this.context == null) {
 
                this.context = this.createWebApplicationContext(servletContext);
 
            }
 
 
 
            if (this.context instanceof ConfigurableWebApplicationContext) {
 
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
 
                if (!cwac.isActive()) {
 
                    if (cwac.getParent() == null) {
 
                        ApplicationContext parent = this.loadParentContext(servletContext);
 
                        cwac.setParent(parent);
 
                    }
 
 
 
                    this.configureAndRefreshWebApplicationContext(cwac, servletContext);
 
                }
 
            }
 
 
 
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
 
            ClassLoader ccl = Thread.currentThread().getContextClassLoader();
 
            if (ccl == ContextLoader.class.getClassLoader()) {
 
                currentContext = this.context;
 
            } else if (ccl != null) {
 
                currentContextPerThread.put(ccl, this.context);
 
            }
 
 
 
            if (logger.isDebugEnabled()) {
 
                logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
 
            }
 
 
 
            if (logger.isInfoEnabled()) {
 
                long elapsedTime = System.currentTimeMillis() - startTime;
 
                logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
 
            }
 
 
 
            return this.context;
 
        } catch (RuntimeException var8) {
 
            logger.error("Context initialization failed", var8);
 
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);
 
            throw var8;
 
        } catch (Error var9) {
 
            logger.error("Context initialization failed", var9);
 
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var9);
 
            throw var9;
 
        }
 
    }
 
}
 

 

 

After analyzing this step, we find that the Spring container is instantiated in this method. Next, let's sort out the overall idea:

When the Servlet container starts, the ServletContext object is initialized, and then the Servlet container calls web Of listeners registered in XML

     public void contextInitialized(ServletContextEvent event)

Method, and in the listener, the this. is called. Initwebapplicationcontext (event. Getservletcontext()) method, in which the Spring IOC container is instantiated. The ApplicationContext object.

Therefore, when the ServletContext is created, we can create the applicationContext object. When the ServletContext is destroyed, we can destroy the applicationContext object. In this way, applicationContext and ServletContext "live and die together".

reference material

[1] Wikipedia Servlet

[2] Servlet, JSP and Spring MVC beginner's Guide [plus] Buid Kurniawan [US] Paul Deck, translated by Lin Yiming and Yu Limin, China industry and information publishing house

Original address

https://blog.csdn.net/qq_19782019/article/details/80292110

Keywords: servlet source code

Added by jsucupira on Tue, 04 Jan 2022 15:55:27 +0200