Detailed analysis of the steps to develop a Servlet from the realization of the Servlet interface, the inheritance of the GenericServlet abstract class, and the inheritance of the HttpServlet abstract class are gradually improved

实现Servlet接口

jakarta.servlet.Servlet is the core interface in the Servlet specification

Servlet对象的生命周期

The creation of the Servlet object, the invocation of the method on the object, and the final destruction of the object are all in the sole responsibility of the Tomcat server, and JavaWeb programmers have no right to intervene

  • Step 1: The Tomcat server is essentially a WEB container. When the server starts, it will parse the XML file and use the reflection mechanism to create a Servlet object based on the full class name of the Servlet class.
  • Step 2: These created Servlet objects will be put into a HashMap collection, the request path is used as the key of the map collection, and the Servlet object is used as the value of the map collection

Only the Servlet objects placed in the HashMap collection created by the Tomcat server can be managed by the WEB container. Our own new Servlet objects are not in the container and are not managed by the WEB container.

insert image description here

Servlet object creation timing: By default, the Servlet object will not be instantiated when the server is started (when the server is started, it is found that the no-argument construction method in the Servlet is not executed)

  • Before the user sends a request, if all Servlet objects are created in advance, it will inevitably consume memory. In addition, if these Servlet objects have not been accessed by users, there is no need to create them first.

Create a Servlet object when the server starts: Add a child tag to the servlet parent tag (the smaller the integer value, the higher the priority)

<servlet>
    <servlet-name>aservlet</servlet-name>
    <servlet-class>com.bjpowernode.javaweb.servlet.AServlet</servlet-class>
    <!--在servlet标签中添加<load-on-startup>子标签,在该子标签中填写整数,越小的整数优先级越高-->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>aservlet</servlet-name>
    <url-pattern>/a</url-pattern>
</servlet-mapping>

Servlet类中的一些方法

If the Servlet class does not provide a parameterless construction method, Tomcat cannot instantiate the Servlet object. At this time, a 500 status will appear, indicating that an error has occurred in the Java program on the server side.

  • In the actual development of Servlet, it is not recommended for programmers to manually define the construction method, because improper definition can easily make the parameterless construction method disappear and make the server unable to instantiate the Servlet object

Is it possible to put the code in the init method in the Servlet into a parameterless construction method (both the parameterless construction method and the init method are executed when the object is first created and only executed once)

  • No, because manually writing the constructor when writing the Servlet class is easy to make the parameterless constructor disappear and the code cannot be executed, so the initialization operation needs to be placed in an init method alone
method name execution time method role usage count
No parameter constructor Executed when the server receives the user's request path How to create a Servlet object Execute only once
init Executed after the Servlet object is created for the first time The method to complete the initialization operation (such as initializing the database connection pool, thread pool, etc.) only needs to be executed once
service As long as the user sends a request, the service method must be executed once The core method of processing user requests, the most used method Send N requests and execute N times
destroy The destroy method is called once before destroying the Servlet object The final preparations before destroying the Servlet object (such as closing the stream and database connection resources opened in the program) Execute only once
getServletInfo Get servlet related information (author, copyright number)
getServletConfig Get the ServletConfig object (encapsulates the initialization parameter information of the Servlet object)

Tests for methods in servlets

public class AServlet implements Servlet {
    
    

    // 无参数构造方法
    public AServlet() {
    
    
        System.out.println("AServlet无参数构造方法执行了");
    }
   
    // init方法通常是完成初始化操作的,init方法在执行的时候AServlet对象已经被创建出来了
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
    
    
        System.out.println("AServlet's init method execute!");
    }

    // 只要用户发送一次请求,service方法必然会执行一次
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    
    
        System.out.println("AServlet's service method execute!");
    }

   
    // destroy方法在执行的时候,AServlet对象的内存还没有被销毁
    @Override
    public void destroy() {
    
    
        System.out.println("AServlet's destroy method execute!");
    }

    //获取Servlet的相关信息(作者,版权号)
    @Override
    public ServletConfig getServletConfig() {
    
    
        return null;
    }

    //获取ServletConfig对象(封装Servelt对象的初始化参数信息)
    @Override
    public String getServletInfo() {
    
    
        return null;
    }
}

Servlet处理请求的流程

When the user sends the first request

  • Step 1: The Tomcat server parses the web.xml file according to the request path to match the corresponding full class name, and then invokes the parameterless construction method through the reflection mechanism to create the Servlet object
  • Step 2: The Tomcat server calls the init method of the Servlet object to complete the initialization (the Servlet object has been created when the init method is executed)
  • Step 3: The Tomcat server calls the service method of the Servlet object to process the request

When the user sends the second or third request, no new Servlet object will be created at this time, and the service method of the previously created Servlet object will be called directly to process the request

  • Although the Servlet object Tomcat only creates one instance at a time, the Servlet class does not conform to the characteristics of the singleton mode (privatization of the construction method), it can only be said to be a false singleton

Since the Tomcat server will release the memory of the Servlet object when the server is closed, the destroy method of the Servlet object will be called at this time to make the final preparations before destruction

  • The AServlet object has not been destroyed when the destroy method is executed, and the memory of the AServlet object will be released by Tomcat after the destroy method is executed.

继承GenericServlet抽象类

jakarta.servlet.GenericServlet is an official standard general Servlet abstract class that does not require us to write it by hand, but we can imitate a handwritten one

适配器设计模式

The mobile phone cannot be directly plugged into the 220V voltage, you can find a charger/adapter (Adapter) to connect the 220V voltage to solve the problem

For the UserService class, the core method is the most important method; for the CustomerService class, m2 is the most important method; other methods are not used in most cases

public interface MyInterface {
    
    
    void m1();
    void m2();
    void m3();
    void core();
}

The adapter of the UserService class is an abstract class that does not implement the main method core()

public abstract class UserAdapter implements MyInterface {
    
    
    @Override
    public void m1() {
    
    

    }
    @Override
    public void m2() {
    
    

    }
    @Override
    public void m3() {
    
    

    }
    //UserService类的主要方法适配器没有去实现
    public abstract void core();
}

// UserService类只需要实现主要的core()方法即可,其他方法适配器已经帮我们实现了
public class UserService extends UserAdapter {
    
    
    @Override
    public void core() {
    
    
    }
}

The adapter dedicated to the CustomerService class is an abstract class that does not implement the main method m2()

public abstract class CustomerAdapter implements MyInterface {
    
    
    @Override
    public void m1() {
    
    

    }
    //CustomerService类的主要方法适配器没有去实现
    public abstract void m2() ;

    @Override
    public void m3() {
    
    

    }
    @Override
    public void core() {
    
    

    }
}
// CustomerService类只需要实现主要的m2()方法即可,其他方法适配器已经帮我们实现了
public class CustomerService extends CustomerAdapter{
    
    
    @Override
    public void m2() {
    
    

    }
}

手写GenericServlet

For the written Servlet class, the service method is often used, and other methods are not needed in most cases. If you directly implement the Servlet interface, you need to implement all the method codes in the interface, which will be very redundant.

Write a standard general abstract class GenericServlet to implement the Servlet interface (this class is an adapter), which has an abstract method service

  • All Servlet classes written in the future should not directly implement the Servlet interface, just inherit GenericServlet and rewrite the service method

The ServletConfig object in the init method in the Servlet class is created by the Tomcat server. When the init method is called, the ServletConfig object will be passed to the init method (the method parameter is a local variable)

public class Tomcat {
    
    
    public static void main(String[] args){
    
    
        // .....
        // 创建LoginServlet对象(通过反射机制调用无参数构造方法来实例化LoginServlet对象)
        Class clazz = Class.forName("com.bjpowernode.javaweb.servlet.LoginServlet");
        Object obj = clazz.newInstance();

        // 向下转型
        Servlet servlet = (Servlet)obj;

        // 创建ServletConfig对象
        // 多态(Tomcat服务器完全实现了Servlet规范)
        ServletConfig servletConfig = new org.apache.catalina.core.StandardWrapperFacade();

        // 调用Servlet的init方法
        servlet.init(servletConfig);

        // 调用Servlet的service方法
        //....
    }
}

// init方法通常是完成初始化操作的,init方法在执行的时候AServlet对象已经被创建出来了
@Override
public void init(ServletConfig servletConfig) throws ServletException {
    
    
    System.out.println("AServlet's init method execute!");
}

How to ensure that the ServletConfig object can be used in the service method

  • When XxxServlet inherits the GenericServlet class, when calling the init method of the XxxServlet object, it will eventually call the init method in the parent class GenericServlet to receive the ServletConfig object (local variable)
  • Declare a member variable for the GenericServlet class and assign the local variable in the init method to the member variable, so that the subclass can access it through the getServletConfig method provided by the parent class
public abstract class GenericServlet implements Servlet {
    
    
    // 成员变量
    private ServletConfig config;

    @Override
    //使用final修饰的init方法子类不能重写
    public final void init(ServletConfig config) throws ServletException {
    
    
        //System.out.println("servletConfig对象,小猫咪创建好的:" + config);
        this.config = config;

        // 调用init()方法,这个this就是Servlet对象,如果子类重写了init方法那么一定是调用子类的init方法
        this.init();
    }


    //这个init方法是供子类重写的
    public void init(){
    
    

    }
    //抽象方法,这个方法最常用。所以要求子类必须实现service方法。
    public abstract void service(ServletRequest servletRequest, ServletResponse servletResponse)
        throws ServletException, IOException;
    
    @Override
    public void destroy() {
    
    

    }
    @Override
    public String getServletInfo() {
    
    
        return null;
    }
    @Override
    public ServletConfig getServletConfig() {
    
    
        return config;
    }
}

The GenericServlet class provides a no-parameter init method for subclasses to rewrite. When the server calls the parameter-less init method, it calls the no-parameter init method. If the subclass rewrites the no-parameter init method, it calls the subclass's

  • If the subclass rewrites the init method with parameters, the init method with parameters in the parent class will not be executed, then the ServletConfig object of the GenericServlet class will be null, causing a null pointer exception in the future
public class LoginServlet extends GenericServlet{
    
    
    @Override
    public void init(){
    
    
        System.out.println("LoginServlet's init() method execute!");
    }
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    
    
        System.out.println("正在处理用户登录请求,请稍后。。。。。");
        // 想在LoginServlet子类中使用ServletConfig对象怎么办?
        ServletConfig config = this.getServletConfig();
        System.out.println("service方法中是否可以获取到ServletConfig对象?" + config);
    }
}

GenericServlet中的方法

GenericServlet implements the basic method of Servlet class

method name Function
getServletConfig() Get the ServletConfig object (encapsulate the initialization parameter information of the Servlet object, that is, the information in the init-param tag)
getServletContext() Get the ServletContext object (the object that encapsulates the context initialization parameter information is the information in the context-param tag)
getServletInfo() Get servlet related information (author, copyright number)
log() Record log information

GenericServlet inherits the extension method of ServletConfig class

  • As long as the Servlet you write inherits GenericServlet, you can use this to call the four methods of the parent class ServletConfig without first obtaining the ServletConfig object.
method name Function
public String getInitParameter(String name) Get the value by the name of the init-param tag
public Enumeration< String > getInitParameterNames() Get the name of all init-param tags
public ServletContext getServletContext() Get the information in the context-param tag
public String getServletName() Get the name of the Servlet object

继承HttpServlet抽象类

When we write the Servlet class in the future, we will not directly inherit the GenericServlet class, but will inherit the HttpServlet (handling the HTTP protocol is more convenient)

  • The B/S structure system is based on the HTTP hypertext transfer protocol, so a Servlet class specially prepared for the HTTP protocol is provided in the Servlet specification called HttpServlet

Classes and interfaces under the jakarta.servlet.http package

  • HttpServletRequest: The Tomcat server parses all the data in the "request protocol", and then encapsulates all the data into the request object
  • HttpServletResponse: The Tomcat server parses all the data in the "response protocol", and then encapsulates all the data into the response object
class or interface effect
jakarta.servlet.http.HttpServlet (abstract class) Servlet class dedicated to HTTP protocol, more convenient to handle HTTP protocol
jakarta.servlet.http.HttpServletRequest (interface) A request object dedicated to the HTTP protocol, which is used to obtain data in the request protocol for HttpServletRequest
jakarta.servlet.http.HttpServletResponse HTTP protocol specific response object

Inheritance structure and source code analysis of the HttpServlet class: The Servlet we write in the future must inherit the HttpServlet class

  • jakarta.servlet.Servlet (interface) [grandpa]
  • jakarta.servlet.GenericServlet implements Servlet (abstract class) [son]
  • jakarta.servlet.http.HttpServlet extends GenericServlet (abstract class) [grandson]

HttpServlet处理请求流程

The first step is that the user initiates a request: the Tomcat server matches the request path, and calls the parameterless construction method through the reflection mechanism to create the corresponding Servlet object.

public class HelloServlet extends HttpServlet {
    
    
    // 用户第一次请求,创建HelloServlet对象的时候会执行这个无参数构造方法
    public HelloServlet() {
    
    
    }
    //override 重写 doGet方法
    //override 重写 doPost方法
}

In the second step, the Tomcat server calls the parameterized init method of the Servlet object to complete the initialization: If this class does not provide this method, nor does the parent class HttpServlet, and finally executes the parameterized init method in the GenericServlet class

public abstract class GenericServlet implements Servlet, ServletConfig,java.io.Serializable {
    
    
    // 用户第一次请求的时候,HelloServlet对象第一次被创建之后这个init方法会执行
    public void init(ServletConfig config) throws ServletException {
    
    
        this.config = config;
        this.init();
    }
    // 用户第一次请求的时候,带有参数的init(ServletConfig config)执行之后会执行这个没有参数的init()
    public void init() throws ServletException {
    
    
        // NOOP by default
    }
}

The third step is that the Tomcat server calls the service method of the Servlet object whose parameters do not contain httpXxx to process the request: If this class does not provide a service method, execute the service method of the parent class HttpServlet class

public abstract class HttpServlet extends GenericServlet {
    
    
    // 用户发送第一次请求的时候这个service会执行, 用户发送第N次请求的时候还是会执行
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
    
    
        HttpServletRequest  request;
        HttpServletResponse response;
        try {
    
    
            // 将ServletRequest和ServletResponse向下转型为带有Http的HttpServletRequest和HttpServletResponse
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) res;
        } catch (ClassCastException e) {
    
    
            throw new ServletException(lStrings.getString("http.non_http"));
        }
        // 调用重载的service模板方法
        service(request, response);
    }
}

The fourth step is to call the service template method provided by the HttpServlet class with overloaded parameters including httpXxx (this method is responsible for defining the core algorithm skeleton, and the specific implementation steps are delayed to subclasses to complete)

protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {
    
    
    // 获取请求方式,可能是GET POST PUT DELETE HEAD OPTIONS TRACE七种之一
    String method = req.getMethod();

    // 如果请求方式是GET请求,则执行doGet方法。
    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)) {
    
    
        // 如果请求方式是POST请求,则执行doPost方法。
        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);
    }
}

The fifth step is that the Tomcat server calls the specific doXxx method to process the request according to the request method: if this class does not provide it, call the doXxx method of the parent class (405 error will be reported at this time)

public class HelloServlet extends HttpServlet {
    
    x
    // 通过无参数构造方法创建对象
    public HelloServlet() {
    
     }
    // 本类没有提供有参的init方法,执行父类HttpServlet的该方法。HttpServlet类中没有该方法,会继续执行GenericServlet类中的该方法
    // 本类没有提供参数不含httpXxx的service方法, 执行父类HttpServlet的该方法
    // 当前端发送的请求是get请求的时候,我这里重写doGet方法
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
    
    
        PrintWriter out = response.getWriter();
        out.print("<h1>doGet</h1>");
    }

    // 当前端发送的请求是post请求的时候,我这里重写doPost方法
    /*protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException{
        PrintWriter out = response.getWriter();
        out.print("<h1>doPost</h1>");
    }*/
}   

HttpServlet细节分析

If Tomcat invokes the specific doXxx method to process the request according to the request method initiated by the front end, and XxxServlet does not provide the corresponding method, it will execute the doXxx method of HttpServlet (it will inevitably report a 405 error)

//HttpServlet中的doXxx方法的作用就是报一个405错误,提醒开发人员编写对应的方法
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException{
    
    
    // 报405错误
    String msg = lStrings.getString("http.method_get_not_supported");
    sendMethodNotAllowed(req, resp, msg);
}

//如果子类没有重写doPost方法 , 则会执行 HttpServlet 的doPost方法
//该方法的作用是报一个 405 错误
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {
    
    
    // 报405错误
    String msg = lStrings.getString("http.method_post_not_supported");
    sendMethodNotAllowed(req, resp, msg);
}

After rewriting the overloaded parameters in the HttpServlet class including the service() template method of httpXxx, you will not be able to enjoy HTTP protocol-specific things such as 405 error reminders

public class HelloServlet extends HttpServlet {
    
    x
    // 通过无参数构造方法创建对象
    public HelloServlet() {
    
    
    }
	// 重写HttpServlet类中重载的service()方法后HttpServlet类中的service()模板方法就不会在执行,里面的doXxx方法也就无法执行 , 就不会报405错误
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.print("<h1>hello servlet</h1>");
    } 
}

Rewriting both the doGet and doPost methods in the Servlet class can indeed avoid the occurrence of 405 errors, but it is not recommended to let him report errors because 405 errors are still useful.

  • If you rewrite doGet and doPost at the same time, it is better to directly rewrite the service template method code and write a little less
  • The back end decides what kind of request the front end needs to send. The back end rewrites the doGet method and the front end must send a get request. The end rewrites the doPost method, and the front end must send a post request.

Servlet类的最终开发步骤

Step 1: Write a Servlet class that directly inherits HttpServlet

The second step: rewrite the doGet method or rewrite the doPost method, whoever rewrites the javaweb programmer has the final say (rewrite a method of the parent class with a shortcut key ctrl + o)

Step 3: Configure the written Servlet class into the web.xml file

Step 4: Prepare the request path initiated by specifying the front-end page

public class HelloServlet extends HttpServlet {
    
    x
    // 通过无参数构造方法创建对象
    public HelloServlet() {
    
     }
    // 本类没有提供有参的init方法,执行父类HttpServlet的该方法。HttpServlet类中没有该方法,会继续执行GenericServlet类中的该方法
    // 本类没有提供参数不含httpXxx的service方法, 执行父类HttpServlet的该方法
    // 当前端发送的请求是get请求的时候,我这里重写doGet方法
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
    
    
        PrintWriter out = response.getWriter();
        out.print("<h1>doGet</h1>");
    }

    // 当前端发送的请求是post请求的时候,我这里重写doPost方法
    /*protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException{
        PrintWriter out = response.getWriter();
        out.print("<h1>doPost</h1>");
    }*/
}   

Guess you like

Origin blog.csdn.net/qq_57005976/article/details/131128974