引言
通过深入分析Spring MVC的源码,我们可以更好地理解其工作原理和内部机制。这有助于我们更好地使用该框架进行Web应用程序的开发,并解决实际开发中遇到的问题。同时,对于学习和研究Spring MVC框架的人来说,阅读源码并进行分析也是一种重要的学习和提升手段。
SpringMVC概述
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。是目前主流的实现MVC设计模式的框架,提供前端路由映射、视图解析等功能.
MVC模式
MVC是Model-View-Controller的缩写,是一种常用的软件设计模式。在这种设计模式中,模型层(Model)负责处理业务逻辑和数据,视图层(View)负责呈现用户界面,而控制器层(Controller)负责处理用户交互。
MVC把三层架构中的UI层再度进行了分化,分成了控制器、视图、实体三个部分,控制器完成页面逻辑,通过实体来与界面层完成通话;而Controller层直接与三层架构中的业务逻辑层进行对话。三层架构和MVC可以共存。 三层是基于业务逻辑来分的,而MVC是基于页面来分的。
特点:
MVC重要特点就是两种分离:
视图和数据模型的分离:使用不同的视图对相同的数据进行展示;分离可视和不可视的组件,能够对模型进行独立测试。因为分离了可视组件减少了外部依赖利于测试。(数据库也是一种外部组件)
视图和表现逻辑(Controller)的分离:Controller是一个表现逻辑的组件,并非一个业务逻辑组件。MVC可以作为表现模式也可以作为建构模式,意味这Controller也可以是业务逻辑。分离逻辑和具体展示,能够对逻辑进行独立测试。
优点:耦合性低;重用性高;生命周期成本低;部署块;可维护性高;有利软件工程化管理。
MVC框架详解可以看这位大佬的博文https://zhuanlan.zhihu.com/p/417635345
SpringMVC的主要组件
DispatcherServlet 前端控制器
前端控制器(也称中央控制器),是整体流程控制的中心,由其调用其它组件处理用户的请求,有效的降低了组件间的耦合性
HandlerMapping 处理器映射器
处理器映射器,负责根据用户请求找到对应具体的Handler处理器和拦截器,并将结果组装成一个HandlerExecutionChain返回
HandlerAdapter 处理器适配器
处理器适配器,通过它对处理器进行执行,基于适配器模式开发,如果使用@RequestMapping注解标识方法,那么执行的就是方法对象
如果处理器类上或方法上使用了@ResponseBody注解,那么就没有以下两步
视图解析器(ViewResolver)
视图解析器,将处理结果生成View视图,给方法返回的视图地址解析添加前后缀,返回完整的视图地址
视图(View)
视图,最终产出结果,常用视图如jsp、html ,视图最终渲染由前端控制器完成
SpringMVC执行流程
前端控制器拦截请求
执行doService方法
首先,当一个请求进入controller前会先被DispatcherServlet拦截,进入doService方法,下面是整个doService方法的代码:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
this.logRequest(request);
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap();
Enumeration<?> attrNames = request.getAttributeNames();
label95:
while(true) {
String attrName;
do {
if (!attrNames.hasMoreElements()) {
break label95;
}
attrName = (String)attrNames.nextElement();
} while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
try {
this.doDispatch(request, response);
} finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
this.restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
this.logRequest()方法
进入doService方法后,首先执行this.logRequest方法,记录请求的详细信息,包括请求的 URL、参数、头信息等。这些信息通常会被记录到日志文件中,以便于后续的分析和调试。
attributesSnapshot属性
attributesSnapshot属性用于存储请求的属性快照。它的作用是在请求处理过程中,捕获请求的属性状态,以便在请求处理结束后进行清理或恢复。
在HttpServletRequest对象中设置属性:
- 将Web应用程序上下文对象设置为HttpServletRequest的属性。这允许在请求处理过程中访问应用程序上下文中的bean和其他资源。
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
- 将区域解析器(LocaleResolver)设置为HttpServletRequest的属性。区域解析器用于解析客户端的区域设置信息,以便在应用程序中进行国际化和本地化。
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
- 将主题解析器(ThemeResolver)设置为HttpServletRequest的属性。主题解析器用于解析客户端的主题信息,以便在应用程序中应用不同的主题样式。
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
- 将主题资源(ThemeSource)设置为HttpServletRequest的属性。主题资源用于提供主题相关的资源,如样式表、图片等。
request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
检查flashMapManager是否为非空。
- 如果不为空,表示存在FlashMap管理器。
- 然后,它使用flashMapManager从请求和响应中检索并更新FlashMap。FlashMap是一种用于在重定向请求之间传递属性的数据结构。
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
- 如果成功检索到输入的FlashMap,则将其设置为请求的属性INPUT_FLASH_MAP_ATTRIBUTE,并使用Collections.unmodifiableMap使其不可修改。
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
- 接着,它创建一个新的空FlashMap,并将其设置为请求的属性OUTPUT_FLASH_MAP_ATTRIBUTE。
- 最后,它将flashMapManager设置为请求的属性FLASH_MAP_MANAGER_ATTRIBUTE。
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
FlashMapManager是Spring MVC中用于管理FlashMap的接口。FlashMap用于在重定向请求之间传递属性,通常用于在重定向后显示一次性消息或数据。FlashMapManager负责在请求之间存储和检索FlashMap,并在适当的时候清除FlashMap。具体实现可以根据需要选择,Spring提供了默认的实现,也可以自定义实现以满足特定需求。
执行doDispatch方法
下面是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 {
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
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 ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
doDispatch方法逐行解析:
(1)将当前的HttpServletRequest对象赋值给processedRequest变量,这样可以在后续的代码中使用processedRequest来代表原始的请求对象。
(2)声明一个HandlerExecutionChain类型的变量mappedHandler,并将其初始化为null。这个变量用于存储处理请求的处理器(handler)以及相关的拦截器链。
(3)声明一个boolean类型的变量multipartRequestParsed,并将其初始化为false。这个变量通常用于跟踪是否已经解析了multipart请求。
(4)通过WebAsyncUtils工具类获取与当前请求相关的WebAsyncManager对象,用于处理异步请求的管理。
(5)声明一个类型为ModelAndView的变量mv,并将其初始化为null。ModelAndView是Spring MVC中用于将模型数据和视图名称封装在一起的类。
(6)声明一个类型为Exception的变量dispatchException,并将其初始化为null。这个变量通常用于存储在请求分派过程中发生的异常。
(7)调用checkMultipart方法对请求进行处理
检查是否为multipart请求或对multipart请求进行解析。处理后的请求对象被赋值给processedRequest变量。
processedRequest = this.checkMultipart(request);
检查处理后的请求对象是否与原始请求对象相同
- 如果不同,则说明multipart请求已经被解析。
multipartRequestParsed = processedRequest != request;
- 如果相同就会执行catch语句抛出异常
(8)调用getHandler获取处理器执行链
(9.1)处理器为空报404找不到处理器异常直接返回,返回前会执行finally中的代码
- 检查是否异步处理已经开始,即asyncManager.isConcurrentHandlingStarted()返回true。
- 如果处理程序不为null,则调用处理程序的applyAfterConcurrentHandlingStarted方法,传入处理后的请求和响应对象,以便在并发处理开始后执行相关操作。
- 如果异步处理未开始,它会检查是否multipart请求已经被解析,即multipartRequestParsed为true。
- 如果是,它调用cleanupMultipart方法对处理后的请求进行清理,通常用于清理multipart请求相关的资源。
(9.2)处理器不为空,调用getHandlerAdapter()
getHandlerAdapter方法源码:
调用supports()方法
判断是够支持给定的处理器,如果找到一个适配器和处理器匹配,将返回这个适配器。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
Iterator var2 = this.handlerAdapters.iterator();
while(var2.hasNext()) {
HandlerAdapter adapter = (HandlerAdapter)var2.next();
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
(10)处理器执行链调用applyPreHandle()执行处理器执行链的前置拦截器
applyPreHandle方法源码:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
// 获取拦截器数组
if (!ObjectUtils.isEmpty(interceptors)) {
// 如果拦截器数组不为空
for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
// 遍历拦截器数组
HandlerInterceptor interceptor = interceptors[i];
// 获取当前拦截器
if (!interceptor.preHandle(request, response, this.handler)) {
// 如果拦截器的preHandle方法返回false
this.triggerAfterCompletion(request, response, (Exception)null);
// 触发afterCompletion方法
return false;
// 返回false
}
}
}
return true;
}
(11)处理器适配器调用handle()方法
将处理器执行链(包括拦截器与处理器)交给适配器,适配器调用handle方法执行处理器执行链以及
(12)处理器执行链调用applyPostHandle()执行处理器执行链的后置拦截器
(13)在没有出现异常的情况下会执行以下代码:
该方法的作用:处理调度结果。它首先检查是否有异常,如果有异常则处理异常;然后根据处理结果进行渲染;最后触发请求完成后的处理。
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
执行doService中的finally代码块
- 首先,它使用WebAsyncUtils.getAsyncManager(request)获取请求的异步管理器,并检查是否并发处理已经开始,即isConcurrentHandlingStarted()返回false。
- 然后,它检查attributesSnapshot是否不为null。
- 如果上述条件都满足,它调用restoreAttributesAfterInclude方法,将之前保存的请求属性恢复到请求中。这通常用于在包含请求处理过程中恢复之前保存的请求属性。
文章来源:https://www.toymoban.com/news/detail-813168.html
参考文章
超详细的!!!MVC架构模式说明 - 一只码农-小俊的文章 - 知乎
https://zhuanlan.zhihu.com/p/417635345文章来源地址https://www.toymoban.com/news/detail-813168.html
到了这里,关于SpringMVC的执行流程与源码分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!