Spring MVC学习总结(四):DispatcherServlet分析

一、搭建测试环境

参考博文:https://blog.csdn.net/weixin_47382783/article/details/112946994?spm=1001.2014.3001.5501

针对上面博文中的环境,作出部分修改:

(1)HelloController类的sayHello()方法中修改如下:

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

(2)helloPage.jsp修改如下:

<%@ 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>

 二、DispatcherServlet分析

1、DispatcherServlet结构分析

DispatcherServlet是SpringMVC的核心,其实质是一个HttpServlet。DispatcherSevlet负责将请求分发,所有的请求都有经过它来统一分发。这里可以简单来看一下它的类结构:

(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 {}

 综合上述,可以得到DispatcherServlet的基本类结构如下:

根据上面的类间关系来看一下它们之间的方法调用:

当前端页面发起请求时,HttpServlet会调用doGet()、doPost()或者service()来处理来自页面的请求。在上面的UML图中可以发现HttpServletBean 继承自HttpServlet ,然而在HttpServletBean中并没有重写doGet()和doPost()方法,如下:

由于FrameworkServlet继承HttpServletBean ,间接继承自HttpServlet 。通过查看FrameworkServlet中的方法,可以找到doGet()和doPost(),也就是说doGet()和doPost()是在FrameworkServlet中进行了重写。查看doGet()和doPost()的代码不难发现,它们都调用了processRequest(request,response),如下: 

    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);
    }

在processRequest(request,response)中又调用了doService(request,response)方法。

 this.doService(request, response);

 doService()方法是一个抽象方法,需要子类实现,DispatcherServlet正是FrameworkServlet的子类。由此,我们也就找到了实际真正的处理逻辑所在。

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

 DispatcherServlet中的service方法中的try代码块中再次调用了doDispatch(request, response);这将是后面分析的重点。

this.doDispatch(request, response);

关于上面不同类中方法的调用关系如下:

 

2、DispatcherServlet的大致处理流程

通过前面的分析已经知道DispatcherServlet的核心在于doDispatch()方法,接下来就看一下这个方法的源码:

    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);
            }

        }

    }

页面发送的请求由DispatcherServlet进行接收,最终调用doDispatch()方法进行处理,其流程如下:

a.getHandler()方法根据当前请求地址找到能处理这个请求的目标处理类(处理器)。

b.getHandlerAdapter()方法根据当前处理器类获取到执行这个处理器方法的适配器。

c.使用获取到的适配器执行目标方法。

d.目标方法执行后会返回一个ModelAndView对象。

e.使用ModelAndView的信息转发到具体的页面,并可以在请求域中取出ModelAndView中的模型数据。

猜你喜欢

转载自blog.csdn.net/weixin_47382783/article/details/113414289