一、搭建测试环境
参考博文: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中的模型数据。