详解SpringMVC从基础到源码

 更新时间:2020年09月20日 09:26:28   作者:沸羊羊_  
这篇文章主要介绍了详解SpringMVC从基础到源码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

认识SpringMVC

SpringMVC 框架是以请求为驱动,围绕 Servlet 设计,将请求发给控制器,然后通过模型对象,分派器来展示请求结果视图。其中核心类是 DispatcherServlet,它是一个 Servlet,顶层是实现的Servlet接口。

SpringMVC 处理请求过程

 

  • 客户端发起请求,会首先经过前端控制器 DispatcherServlet 进行转发,转发到 Handler Mapping
  • DispatcherServlet 从 Handler Mapping 查找处理请求的 Controller,Handler Mapping 作用就是完成 URL 到 Controller 的映射
  • Controller 处理请求并返回 ModelAndView 对象,ModelAndView 是封装结果视图的组件
  • 再将视图结果返回给客户端

Servlet 与 SpringMVC

SpringMVC 是在 Servlet 的基础上进行了扩展,看看他们的继承关系是什么样的。

Servlet 继承关系

在这里插入图片描述

SpringMVC 继承关系

在这里插入图片描述

Servlet 与 SpringMVC 对比

  • Servlet 需要每个请求都在 web.xml 文件中配置一个 sevlet 节点
  • SpringMVC 的 DispatcherServlet 会拦截所有请求,让 Controller 去处理

Structs2 与 Spring MVC

相同点

都是基于MVC模型的

不同点

  • Structs2 是基于类的,一个 request 创建一个 action,一个action 对应一个 request ;Servlet 是基于方法的,也就是一个 request 对应一个方法
  • Structs2 入口是 Filter;SpringMVC 入口是 Servlet
  • SpringMVC 的开发速度和性能优于 Structs2 ,流程更易理解
  • SpringMVC 和 Spring 是无缝的,可以认为 SpringMVC 是100% 零配置

SpringMVC源码分析

1、ApplicationContext 初始化时建立所有的URL和Controller 的对应关系

/**
* 建立当前ApplicationContext中的所有controller和url的对应关系
*/
protected void detectHandlers() throws BeansException {
  //日志级别是否是 Debug
  if (logger.isDebugEnabled()) {
    //应用上下文就是工程的访问路径
    logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
  }
     // 获取ApplicationContext容器中所有bean的Name---Controller
  String[] beanNames = (this.detectHandlersInAncestorContexts ?
      BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
      getApplicationContext().getBeanNamesForType(Object.class));

  // 遍历beanNames,并找到这些bean对应的url
  for (String beanName : beanNames) {
       // 获取Controller上的所有url(类上的url+方法上的url)
    String[] urls = determineUrlsForHandler(beanName);
    if (!ObjectUtils.isEmpty(urls)) {
      // 保存urls和beanName的对应关系,put it to Map<urls,beanName>,该方法在父类AbstractUrlHandlerMapping中实现
      registerHandler(urls, beanName);
    }
    else {
      if (logger.isDebugEnabled()) {
        logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
      }
    }
  }
}

2、根据访问URL找到对应 Controller 中处理请求的方法

/** 中央控制器,控制请求的转发 **/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
int interceptorIndex = -1;

try {
  ModelAndView mv;
  boolean errorView = false;
  try {
    // 1.检查是否是文件上传的请求
    processedRequest = checkMultipart(request);

    // 2.取得处理当前请求的controller,这里也称为hanlder,处理器,第一个步骤的意义就在这里体现了.
    这里并不是直接返回controller,而是返回的HandlerExecutionChain请求处理器链对象,该对象封装了handler和interceptors.
    mappedHandler = getHandler(processedRequest, false);
    // 如果handler为空,则返回404
    if (mappedHandler == null || mappedHandler.getHandler() == null) {
      noHandlerFound(processedRequest, response);
      return;
    }
    //3. 获取处理request的处理器适配器handler adapter 
    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 (logger.isDebugEnabled()) {
        String requestUri = urlPathHelper.getRequestUri(request);
        logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
      }
      if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
        return;
      }
    }

    // 4.拦截器的预处理方法
    HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
    if (interceptors != null) {
      for (int i = 0; i < interceptors.length; i++) {
        HandlerInterceptor interceptor = interceptors[i];
        if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
          triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
          return;
        }
        interceptorIndex = i;
      }
    }

    // 5.实际的处理器处理请求,返回结果视图对象
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

    // 结果视图对象的处理
    if (mv != null && !mv.hasView()) {
      mv.setViewName(getDefaultViewName(request));
    }

    // 6.拦截器的后处理方法
    if (interceptors != null) {
      for (int i = interceptors.length - 1; i >= 0; i--) {
        HandlerInterceptor interceptor = interceptors[i];
        interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
      }
    }
  }
  catch (ModelAndViewDefiningException ex) {
    logger.debug("ModelAndViewDefiningException encountered", ex);
    mv = ex.getModelAndView();
  }
  catch (Exception ex) {
    Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
    mv = processHandlerException(processedRequest, response, handler, ex);
    errorView = (mv != null);
  }

  
  if (mv != null && !mv.wasCleared()) {
    render(mv, processedRequest, response);
    if (errorView) {
      WebUtils.clearErrorRequestAttributes(request);
    }
  }
  else {
    if (logger.isDebugEnabled()) {
      logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
          "': assuming HandlerAdapter completed request handling");
    }
  }

  // 请求成功响应之后的方法
  triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
}

3、反射调用处理请求的方法返回结果视图

/** 获取处理请求的方法,执行并返回结果视图 **/
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
	throws Exception {
  // 1.获取方法解析器
  ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);     
  // 2.解析request中的url,获取处理request的方法 
  //通过 request 找 controller 中的处理方法,request的url与 controller 的url 进行匹配
  Method handlerMethod = methodResolver.resolveHandlerMethod(request);     
  // 3.方法调用器
  ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
  ServletWebRequest webRequest = new ServletWebRequest(request, response);
  ExtendedModelMap implicitModel = new BindingAwareModelMap();
     // 4.执行方法
  Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);     
  // 5.封装结果视图
  ModelAndView mav =
      methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
  methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
  return mav;
}

到此这篇关于详解SpringMVC从基础到源码的文章就介绍到这了,更多相关SpringMVC 源码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 深入理解Java的接口与抽象类

    深入理解Java的接口与抽象类

    本文主要介绍java 的接口和抽象类,对接口和抽象类进行介绍对比,深入理解,有需要的小伙伴可以参考下
    2016-07-07
  • java转化为exe程序步骤详解

    java转化为exe程序步骤详解

    在本篇内容里我们给大家分享了关于java转化为exe程序的具体步骤和相关知识点,需要的朋友们学习下。
    2019-03-03
  • SpringBoot启动执行sql脚本的3种方法实例

    SpringBoot启动执行sql脚本的3种方法实例

    在应用程序启动后,可以自动执行建库、建表等SQL脚本,下面这篇文章主要给大家介绍了关于SpringBoot启动执行sql脚本的3种方法,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-01-01
  • Java程序顺序结构中逻辑控制语句详解流程

    Java程序顺序结构中逻辑控制语句详解流程

    在程序开发的过程之中一共会存在有三种程序逻辑:顺序结构、分支结构、循环结构,对于之前所编写的代码大部分都是顺序结构的定义,即:所有的程序将按照定义的代码顺序依次执行
    2021-10-10
  • java8 集合求差集、并集、交集的实例

    java8 集合求差集、并集、交集的实例

    下面小编就为大家分享一篇java8 集合求差集、并集、交集的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2017-12-12
  • Java中使用MyBatis-Plus操作数据库的实例

    Java中使用MyBatis-Plus操作数据库的实例

    本文主要介绍了Java中使用MyBatis-Plus操作数据库的实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-02-02
  • 详解java动态代理的2种实现方式

    详解java动态代理的2种实现方式

    目前Java开发包中包含了对动态代理的支持,但是其实现只支持对接口的的实现。这篇文章主要介绍了详解java动态代理的2种实现方式 ,有兴趣的可以了解一下。
    2016-11-11
  • SpringBoot中使用Jsoup爬取网站数据的方法

    SpringBoot中使用Jsoup爬取网站数据的方法

    这篇文章主要介绍了SpringBoot中使用Jsoup爬取网站数据的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • 深入浅析drools中Fact的equality modes

    深入浅析drools中Fact的equality modes

    这篇文章主要介绍了drools中Fact的equality modes的相关知识,本文通过图文实例代码相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-05-05
  • Java如何判断整数溢出,溢出后怎么得到提示

    Java如何判断整数溢出,溢出后怎么得到提示

    这篇文章主要介绍了Java如何判断整数溢出,溢出后怎么得到提示,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10

最新评论