SpringMVC运行机制详解——学好基本功,走路好轻松

说明:了解SpringMVC运行机制,有利于加深对SpringMVC框架的理解、在开发过程中能够快速定位到问题所在。同时对搭建SSM项目基础配置更加理解。总之,好处多多;

SpringMVC基本概念

SpringMVC框架:属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面(来自百度百科)。总而言之,SpringMVC框架属于Spring架构中的组成部分。在实际开发中,它主要负责HTTP请求接收、数据处理、请求响应等web层功能实现;故SpringMVC也称为Web层框架;它的设计架构是基于MVC架构。

MVC架构概念

M(Model)模型、V(View)视图、C(Controller)控制器三层分离的软件设计思想;主要目的就让M模型层和V视图层进行分离,只专注实现自己的功能。它们之间的交互的通过C控制器层进行协调交。MVC分层的设计思想降低了代码之间的耦合,有利于代码维护。MVC架构图如下所示:


image

耦合度:是指业务代码之间相互依赖、不同功能代码分层不明确。杂糅在一起,类似于的线团,如下图所示:左图就是耦合度高的、有图就是耦合度低的;

SpringMVC基本结构

SpringMVC好比一辆车,由很多部件一起结合组成SpringMVC框架。组成SpringMVC部件主要有DispatcherServlet(分发器)、HandlerMapping(处理映射器)、HandlerAdapter(处理适配器)、ViewResolver(视图解析器)等主要部件组成。

  1. DispatcherServlet(分发器):它属于SpringMVC核心,好比汽车的发动机的重要性。主要用来接收请求、相应结果、调度其他组件的处理/响应请求。由于它的存在,降低了各个组件之间的耦合;
  2. HandlerMapping(处理映射器):根据请求的URL去匹配程序中以“配置/注解”的方式配置的Handler,然后返回Handler处理器链。
  3. HandlerAdapter(处理适配器):根据规则去执行相应的Handler处理器,返回的是处理器处理后的结果;
  4. ViewResolver(视图解析器):将适配器返回的结果ModelAndView,解析成相应的View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。

总结:以上主要组件都是围绕着DispatcherServlet(分发器)进行调度的。它们之间不能直接交互。SpringMVC流程图如下所示:

在这里插入图片描述

SpringMVC源码分析(看不懂记住上面就够了,等知识储备多了再学习)

  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

建议用Idea查看代码,方便的不要不要的。用ctrl+鼠标单击DispatcherServlet进入源码文件;先来看DispatcherServlet源码继承/实现关系图。如下图所示:


在这里插入图片描述

说明:以下根据源码分析DispatcherServlet运行机制流程,不会很具体讲解各个方法的作用;

第一问:DispatcherServlet怎样得到用户的HTTP请求?

从上图可知,DispatcherServlet继承了HttpServlet,用过struts的都知道HtppServlet的作用。它是用处理HTTP请求的。具体自行百度。HttpServlet.class源码方法汇总如下:


在这里插入图片描述

注意红线标记的地方。类DispatcherServlet继承了抽象类FrameworkServlet。并且在抽象类FrameworkServlet中覆盖了HttpServlet中的service方法。覆盖方法源码如下:

/**
*FrameworkServlet.class源码(行数:432——440)
**/
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        if (httpMethod != HttpMethod.PATCH && httpMethod != null) {
            super.service(request, response);
        } else {
            this.processRequest(request, response);
        }

    }

这说明了当HTTP请求过来时,会执行以上方法,如果HTTP请求正常,则调用HttpServlet类中service方法。判断是否请求类型,然后回调FrameworkServlet抽象类中的doGet/doPost方法。最终执行processRequest方法。processRequest源码如下图所示:


在这里插入图片描述

注意图标记的地方;doService是抽象类FrameworkServlet中的抽象方法。并且在子类DispatcherServlet中实现了。

总结:到这为止,知道了DispatcherServlet是通过继承抽象类FrameworkServlet,抽象类FrameworkServlet重写了HttpServlet类的service方法得到HTTP请求;

第二问:HandlerMapping、HandlerAdapte和ViewResolver是怎样初始化?
先看HttpServletBean.class类中init方法源码。如下所示:

/**
* HttpServletBean.class中66-85行
**/
public final void init() throws ServletException {
        PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
            try {
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
                this.initBeanWrapper(bw);
                bw.setPropertyValues(pvs, true);
            } catch (BeansException var4) {
                if (this.logger.isErrorEnabled()) {
                    this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
                }

                throw var4;
            }
        }

        this.initServletBean();
    }

类HttpServletBean中的init()重写了类servlet中的init方法。init 方法的执行时刻跟servlet 配置中的load-on-startup标签有关,如下图示所示:


在这里插入图片描述

如果值大于等于 0,则在 Servlet 实例化的时候执行,间隔时间由具体的值决定,值越大,则越迟执行。如果小于 0 或者没有配置,则在第一次请求的时候才同步执行 (该方法只执行一次);
其中init方法中涉及到了BeanWrapper,PropertyValues,ResourceLoader,initServletBean方法

  1. PropertyValues:获取Web.xml里面的servlet的init-param;
  2. BeanWrapper:封装了bean的行为,提供了设置和获取属性值,它有对应的BeanWrapperImpl;
  3. ResourceLoader:接口仅有一个getResource(String location)的方法,可以根据一个资源地址加载文件资源。classpath:这种方式指定SpringMVC框架bean配置文件的来源;
  4. initServletBean():在HttpServletBean类只是提供了参考,具体实现实在子类FrameworkServlet中。源码如下:


    在这里插入图片描述

initServletBean方法调用了initWebApplicationContext方法。用来初始化springMVC上下文环境。同时通过synchronized关键同步执行onRefresh()方法。源码如下:


在这里插入图片描述

onRefresh方法在FrameworkServlet只是提供参考,具体实现实在子类DispatcherServlet中。源码如下:


在这里插入图片描述

onRefresh方法中调用了initStrategies方法。在initStrategies初始化了SpringMVC的组件。
//初始化上传文件解析器
initMultipartResolver(context);

//初始化本地解析器
initLocaleResolver(context);

//初始化主题解析器
initThemeResolver(context);

//初始化映射处理器
initHandlerMappings(context);

//初始化适配器处理器
initHandlerAdapters(context);

//初始化异常处理器
initHandlerExceptionResolvers(context);

//初始化请求到视图名翻译器
initRequestToViewNameTranslator(context);

//初始化视图解析器
initViewResolvers(context);

总结:目前分析的源码中只涉及了3个类,分别是:FrameworkServlet、DispatcherServlet、HttpServletBean。它们各自做了什么事情?

  1. HttpServletBean是主要是获取web.xml配置文件中的<init-param>标签的值;
  2. FrameworkServlet主要实现了初始化SpringMVC上下文环境;
  3. DispatcherServlet主要实现了SpringMVC各个组件的初始化;
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容