Spring MVC learning summary (four): DispatcherServlet analysis

1. Build a test environment

Reference blog post: https://blog.csdn.net/weixin_47382783/article/details/112946994?spm=1001.2014.3001.5501

For the environment in the above blog post, some modifications are made:

(1) The sayHello() method of the HelloController class is modified as follows:

@Controller
public class HelloController {
    @RequestMapping("/hello")
    public String sayHello(){
        System.out.println("进入到sayHello方法");
        return "helloPage";
    }
}

(2) The helloPage.jsp is modified as follows:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>helloPage</title>
</head>
<body>
<h3>我是helloPage,你好</h3>
<%System.out.println("进入到页面,我是helloPage页面输出");%>
</body>
</html>

 Two, DispatcherServlet analysis

1. Structural analysis of DispatcherServlet

DispatcherServlet is the core of SpringMVC, and its essence is an HttpServlet. DispatcherSevlet is responsible for distributing requests, and all requests are distributed uniformly through it. Here is a brief look at its class structure:

(1)DispatcherServlet继承FrameworkServlet。

public class DispatcherServlet extends FrameworkServlet {}

 (2)FrameworkServlet继承HttpServletBean 。

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {}

(3)HttpServletBean 继承HttpServlet 

public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {}

 Based on the above, the basic class structure of DispatcherServlet can be obtained as follows:

Look at the method calls between them based on the relationship between the above classes:

When the front-end page initiates a request, HttpServlet will call doGet(), doPost() or service() to process the request from the page. In the above UML diagram, it can be found that HttpServletBean inherits from HttpServlet, but the doGet() and doPost() methods are not rewritten in HttpServletBean, as follows:

Because FrameworkServlet inherits HttpServletBean, it indirectly inherits from HttpServlet. By looking at the methods in FrameworkServlet, you can find doGet() and doPost(), that is to say, doGet() and doPost() are rewritten in FrameworkServlet. Looking at the code of doGet() and doPost(), it is not difficult to find that they both call processRequest(request,response), as follows: 

    protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.processRequest(request, response);
    }

    protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.processRequest(request, response);
    }

The doService(request, response) method is called in processRequest(request, response).

 this.doService(request, response);

 The doService() method is an abstract method and needs to be implemented by a subclass. DispatcherServlet is a subclass of FrameworkServlet. From this, we have also found the actual real processing logic.

    protected abstract void doService(HttpServletRequest var1, HttpServletResponse var2) throws Exception;

 DoDispatch(request, response) is called again in the try code block in the service method of DispatcherServlet; this will be the focus of the subsequent analysis.

this.doDispatch(request, response);

The calling relationships of the methods in the above different classes are as follows:

 

2. The general processing flow of DispatcherServlet

Through the previous analysis, we know that the core of DispatcherServlet lies in the doDispatch() method. Next, let’s take a look at the source code of this method:

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                //1,检查是否是文件上传请求
                processedRequest = this.checkMultipart(request);
                multipartRequestParsed = processedRequest != request;
                //2,根据当前的请求地址找到哪个类来处理
                mappedHandler = this.getHandler(processedRequest);
                //3,如果没有找到哪个处理器(控制器)能处理处理这个请求,就404或者抛出异常
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    this.noHandlerFound(processedRequest, response);
                    return;
                }
                //4,拿到执行这个类的所有方法的适配器。
                HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    }

                    if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
                //5,适配器来执行目标方法:将目标方法执行完成后的返回值作为视图名,设置保存在ModelAndView中。无论目标方法怎么写,最终适配器执行完成之后都会将执行后的信息封装成ModelAndView
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                this.applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            } catch (Exception var19) {
                dispatchException = var19;
            }
            //6,根据方法最终执行完成后封装的ModelAndView转发到对应页面,而且ModelAndView中的数据可以从请求域中获取。
            this.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        } catch (Exception var20) {
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, var20);
        } catch (Error var21) {
            this.triggerAfterCompletionWithError(processedRequest, response, mappedHandler, var21);
        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }

    }

The request sent by the page is received by the DispatcherServlet, and finally the doDispatch() method is called for processing. The process is as follows:

a.getHandler() method finds the target processing class (processor) that can handle this request according to the current request address.

b. The getHandlerAdapter() method obtains the adapter that executes this handler method according to the current handler class.

c. Use the acquired adapter to execute the target method.

d. After the target method is executed, a ModelAndView object will be returned.

e. Use the information of ModelAndView to forward to a specific page, and you can retrieve the model data in ModelAndView in the request field.

 

Guess you like

Origin blog.csdn.net/weixin_47382783/article/details/113414289