一文讲述Spring MVC的执行流程

简介: 一文讲述Spring MVC的执行流程

来自官网:

在网上找到的更加详细的图:

我们搜索下DispatchServlet类看看有没有,果然有一个

看下DispatchServlet的类关系图

1 研究DispatchServlet

看下这个类的全部方法,不用想一定会有一个类似于Servlet中的service方法,果然如此

1.1 DispatchServlet的doService方法

在doService方法中一定是主要处理业务请求的

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
   // 记录日志
   logRequest(request);
   // 保留请求属性的快照,以便在包含之后能够恢复原始属性  
   Map<String, Object> attributesSnapshot = null;
   if (WebUtils.isIncludeRequest(request)) {
      attributesSnapshot = new HashMap<>();
      Enumeration<?> attrNames = request.getAttributeNames();
      while (attrNames.hasMoreElements()) {
         String attrName = (String) attrNames.nextElement();
         if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
            attributesSnapshot.put(attrName, request.getAttribute(attrName));
         }
      }
   }
   // 设置一些request的值,使框架对象对处理程序和视图对象可用
   request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());  
   ......
   try {
      //最主要的doDispatch方法
      doDispatch(request, response);
   }
   ......
}
1.2 DispatchServlet的doDispatch方法

doDispatch是被doService方法调用,是处理请求流程的一个主要方法,主要分为一下几个流程

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   ......
   try {
      ModelAndView mv = null;
      Exception dispatchException = null;
      try {
         //检查处理Multipart请求
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);
         // 确定当前请求的处理,请求Handler
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }
         // 获取适配器
         HandlerAdapter ha = 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;
         }
         // 实际的Handler调用
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }
         // 解析ModelAndView
         applyDefaultViewName(processedRequest, mv);
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      ......
      // 渲染视图
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   ......
   finally {
      ......
   }
}

2 深入源码

2.1 怎样找到Handler的?

先写一个RequestMapping

@ResponseBody
@RequestMapping("/helloMvc")
public String helloMvc() {
    return "Hello I am Spring MVC";
}

打断点,debug

请求

开始debug

点进方法内部再看看

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   if (this.handlerMappings != null) {
       // 遍历所有的request,直到最后都没有的话返回null
       for (HandlerMapping mapping : this.handlerMappings) {
         HandlerExecutionChain handler = mapping.getHandler(request);
         if (handler != null) {
            return handler;
         }
      }
   }
   return null;
}

在这之后,请求就会找到对应的Handler进行处理。

2.2 Spring MVC视图解析机制

首先定义一个ViewResolver类型的List

/** List of ViewResolvers used by this servlet. */
@Nullable
private List<ViewResolver> viewResolvers;

初始化ViewResolvers

private void initViewResolvers(ApplicationContext context) {
   this.viewResolvers = null;
   if (this.detectAllViewResolvers) {
      // 在ApplicationContext中找到所有的viewresolver,包括全部上下文
      Map<String, ViewResolver> matchingBeans =
            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
      if (!matchingBeans.isEmpty()) {
         this.viewResolvers = new ArrayList<>(matchingBeans.values());
         // 保持viewresolver的排序。
         AnnotationAwareOrderComparator.sort(this.viewResolvers);
      }
   }
   ......
}

然后进行视图解析

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
      @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
      @Nullable Exception exception) throws Exception {
   boolean errorView = false;
   // 判断有无异常
   if (exception != null) {
      // 如果异常不为空,则返回一个错误的页面
      if (exception instanceof ModelAndViewDefiningException) {
         logger.debug("ModelAndViewDefiningException encountered", exception);
         mv = ((ModelAndViewDefiningException) exception).getModelAndView();
      }else {
         Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
         mv = processHandlerException(request, response, handler, exception);
         errorView = (mv != null);
      }
   }
   if (mv != null && !mv.wasCleared()) {
      // 视图渲染
      render(mv, request, response);
      if (errorView) {
         WebUtils.clearErrorRequestAttributes(request);
      }
   }
   ......
}

视图渲染

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
   // 确定请求的区域设置并将其应用于响应
   Locale locale =
         (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
   response.setLocale(locale);
   View view;
   String viewName = mv.getViewName();
   if (viewName != null) {
      // 获取视图名称
      view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
      if (view == null) {
         throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
               "' in servlet with name '" + getServletName() + "'");
      }
   }
   else {
      // No need to lookup: the ModelAndView object contains the actual View object.
      view = mv.getView();
      if (view == null) {
         throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
               "View object in servlet with name '" + getServletName() + "'");
      }
   }
   ......
   try {
      if (mv.getStatus() != null) {
         response.setStatus(mv.getStatus().value());
      }
      view.render(mv.getModelInternal(), request, response);
   }
   catch (Exception ex) {
      if (logger.isDebugEnabled()) {
         logger.debug("Error rendering view [" + view + "]", ex);
      }
      throw ex;
   }
}


相关文章
|
14天前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
40 2
|
3月前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
283 24
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
2月前
|
JSON 前端开发 Java
SSM:SpringMVC
本文介绍了SpringMVC的依赖配置、请求参数处理、注解开发、JSON处理、拦截器、文件上传下载以及相关注意事项。首先,需要在`pom.xml`中添加必要的依赖,包括Servlet、JSTL、Spring Web MVC等。接着,在`web.xml`中配置DispatcherServlet,并设置Spring MVC的相关配置,如组件扫描、默认Servlet处理器等。然后,通过`@RequestMapping`等注解处理请求参数,使用`@ResponseBody`返回JSON数据。此外,还介绍了如何创建和配置拦截器、文件上传下载的功能,并强调了JSP文件的放置位置,避免404错误。
|
2月前
|
前端开发 Java 应用服务中间件
【Spring】Spring MVC的项目准备和连接建立
【Spring】Spring MVC的项目准备和连接建立
60 2
|
2月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
207 2
|
3月前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
2月前
|
XML 前端开发 Java
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
本文阐述了Spring、Spring Boot和Spring MVC的关系与区别,指出Spring是一个轻量级、一站式、模块化的应用程序开发框架,Spring MVC是Spring的一个子框架,专注于Web应用和网络接口开发,而Spring Boot则是对Spring的封装,用于简化Spring应用的开发。
161 0
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
|
3月前
|
XML 缓存 前端开发
springMVC02,restful风格,请求转发和重定向
文章介绍了RESTful风格的基本概念和特点,并展示了如何使用SpringMVC实现RESTful风格的请求处理。同时,文章还讨论了SpringMVC中的请求转发和重定向的实现方式,并通过具体代码示例进行了说明。
springMVC02,restful风格,请求转发和重定向
|
2月前
|
JSON 前端开发 JavaScript
优雅!Spring Boot 3.3 实现职责链模式,轻松应对电商订单流程
本文介绍如何使用 Spring Boot 3.3 实现职责链模式,优化电商订单处理流程。通过将订单处理的各个环节(如库存校验、优惠券核验、支付处理等)封装为独立的处理器,并通过职责链将这些处理器串联起来,实现了代码的解耦和灵活扩展。具体实现包括订单请求类 `OrderRequest`、抽象处理器类 `OrderHandler`、具体处理器实现(如 `OrderValidationHandler`、`VerifyCouponHandler` 等)、以及初始化职责链的配置类 `OrderChainConfig`。
|
4月前
|
Java 数据库连接 Spring
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
文章是关于Spring、SpringMVC、Mybatis三个后端框架的超详细入门教程,包括基础知识讲解、代码案例及SSM框架整合的实战应用,旨在帮助读者全面理解并掌握这些框架的使用。
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】