一、概述
MVC全称为Model View Controller,是一种用于创建Web应用程序表现层的设计模式。今天我们从源码的角度介绍一下Spring MVC。
二、项目构建
和之前介绍Spring框架一样,我们要了解Spring MVC的运行原理需要先搭建一个简单的项目,通过项目来加深自己对该框架的理解。下面我们搭建一个简单的Spring MVC项目,如下图
我们先运行代码,看看Spring MCV架构的优势和特点在哪里!我们先看看该项目的代码量,通过下图我们可以看到除了配置文件以外,我们只写了DemoController这一个类便可以请求到success.jsp这个页面。如果我们要想再访问一个页面,只需要在该类里再添加一个方法用@RequestMapping注解标记即可。
我们再看下之前的spring框架怎么进行访问的……如下图
该访问即为最初的访问方式,通过实现HttpServlet,重写doGet和doPost方法来实现访问,这种方式每一个请求都要有一个具体的类去实现HttpServlet,重写里面的doPost和doGet方法做具体的业务处理……
通过这两种方式的比较,我们可以很轻易的看出Spring MVC解放了生产力,让开发者可以花更少的时间去实现更多的价值。那么它究竟是如何实现的呢?我们接下来通过阅读源码的方式来了解一下它的运行原理。
三、源码剖析
同样和解析Spring框架时的做法一样,我们先观察web.xml配置文件
从配置文件中我们可以看到里面配置了一个servlet和servlet-mapping,servlet类为DispatcherServlet,参数为我们的springmvc.xml配置文件;servlet-mapping配置了路径的映射,然后我们再来看看springmvc.xml文件的内容
我们配置了注解扫描和视图解析器,了解完配置文件后,我们来解析下DispatcherServlet这个类,其实该类的核心是doService方法如下图
doService方法的核心又是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 {
// 检查是否为文件上传请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 获取处理当前请求的handler处理器
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 没有获取到handler处理器,就返回404
noHandlerFound(processedRequest, response);
return;
}
// 获取当前请求的处理程序适配器。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 处理 last-modified 请求头
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 实际处理器处理请求,返回 ModelAndView 对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 对结果的视图对象进行处理
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 跳转页面渲染视图
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
Spring MVC的核心思想全部在这个方法里面,先通过扫描获取@Controller标记的类,即handler,没找到则404报错,通过适配器找到能处理该请求的处理器,然后实际请求返回ModelAndView对象,最后对该对象进行处理,渲染,然后跳转到该页面。
四、小结
本章我们简单的构建了一个Spring MVC项目,发现使用该框架可以避免频繁的创建servlet,减少开发者的工作量。通过解读DispatcherServlet这个类,我们从整体上知道了Spring MVC的运作模式,下一篇文章我们就详细的解读一下handler和handlerAdapter是怎么运作的。
感兴趣的读者朋友可以 关注本公众号,和我们一起学习探究。
本人因所学有限,如有错误之处,望请各位指正!