概述

目前 Spring、Springboot 、 Springmvc 算是传统互联网非常常见的技术,Springmvc 重要性向来不如前两个,但是也不影响我们应该更完善的了解Springmvc到底是什么。毕竟一个传统的马工,调试接口天经地义,我们都听过面试造航母,工作拧螺丝。接口就是我们平常工作最锃光瓦亮的那一颗螺丝,如果能理解Springmvc,相信你的工作完成起来能更加快速高效 ,这样就能腾出更多的时间学别的~

实现

在Spring的具体实现上,子容器和父容器都是通过ServletContext的setAttribute方法放到ServletContext中的。在以往依靠tomcat的监听器和Servlet去启动的条件下,ContextLoaderListener会先于DispatcherServlet创建ApplicationContext,DispatcherServlet在创建ApplicationContext时会先找到由ContextLoaderListener所创建的ApplicationContext,再将后者的ApplicationContext作为参数传给DispatcherServlet的ApplicationContext的setParent()方法。也就是说,子容器的创建依赖于父容器的创建,父容器先于子容器创建。在Spring源代码中,你可以在FrameworkServlet中找到如下代码:

wac.setParent(parent);

但是在Springboot将tomcat作为嵌入式容器之后,有了一些改变,先看一下DispatcherServlet是什么时候创建的

小知识:springboot中,DispatcherServlet是什么时候创建的?
从refresh()开始(重要的启动入口),执行到this.onRefresh()然后如下:
tomcatServletWebServerFactory在创建过程中,触发ErrorPageRegistrarBeanPostProcessor后置处理器,创建errorPageCustomizer 在解决属性注入时创建了errorPageCustomizer 解决依赖DispatcherServletPath,创建了其实现类 dispatcherServletRegistration,为了解决依赖创建了DispatcherServlet ;

SpringBoot使用tomcat作为嵌入式servlet容器,在onRefresh()这里完成了嵌入式Servlet容器的创建

我们只关注DispatcherServlet的创建时间点,是在refresh()中的this.onRefresh()创建的就够了,看过源码都知道如下代码完成了Spring IOC容器的创建,那么在此之前 tomcat已经先于执行了。

this.finishBeanFactoryInitialization(beanFactory);

在执行完成上述以后,容器已经创建完成。

DispatcherServlet属性赋值

我们回头看一眼重要的属性赋值,在DispatcherServlet创建过程中,FrameworkServlet的重要属性也赋值完成,如下:
ApplicationContextAwareProcessor去创建,是使用ApplicationContextAware,这里不详细解释,可以看看之前我写的这篇内容(其实写这一篇只是心血来潮)

ApplicationContextAware底层原理详解

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware

通过这种方式就Spring ioc容器注入到了FrameworkServlet的WebApplicationContext属性中了,对于之后的调用,我们就能理解为什么父容器和子容器其实是一个容器的事实。

WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;if (this.webApplicationContext != null) {// A context instance was injected at construction time -> use itwac = this.webApplicationContext;

其中,wac即为由DisptcherServlet的父类FrameworkServlet的属性的webApplicationContext,而parent则为由ContextLoaderListener创建的ApplicationContext(这里只是便于理解,其实已经不会执行到setParent()的内容了)。
此后,框架又会调用ServletContext的setAttribute()方法将wac加入到ServletContext中的属性中
在FrameworkServlet中,上图就是作为类中属性存在的容器,WebApplicationContext是ApplicationContext的子接口;

父容器是以org.springframework.web.context.WebApplicationContext.ROOT 作为ServletContext(web应用上下文)的key保存容器。

子容器是以org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcherServlet作为key保存容器

扩展

下图 rootContext是 父容器的容器,下面的DefaultListableBeanFactory类可能好多人都见到过,这里主要是帮助理解,在rootContext的内容如下


说到DefaultListableBeanFactory, 简单介绍一下DefaultSingletonBeanRegistry ,它们是继承的关系,DefaultListableBeanFactory是子类,DefaultSingletonBeanRegistry是父类。这里DefaultSingletonBeanRegistry有几个特别重要的属性
几个属性从上到下依次为一级缓存,三级缓存和二级缓存。听说过三级缓存解决循环依赖吗,指的就是这里的三级缓存。不过多解释,一级缓存是存放已经初始化和实例化好的对象,二级缓存是存放只是实例化好但并没有初始化完成的对象,而三级缓存,放的根本不是对象,而是供循环依赖时调用产生对象的ObjectFactory。这里主要是为了方便理解 singletonObjects是一级缓存;

注: 我们平时getBean取得的对象就是来自于一级缓存

父子容器

介绍这些主要是为了引出 父子容器的关系:

  1. 首先直观的去比较一级缓存中的数据,这里是Spring容器真实创建好的bean

2. 比较wac和rootContext的对象引用
得出结论:wac和rootContext是同一个对象,都是Spring创建好的ioc容器
至此,我们知道在Springboot中,我们的子容器和父容器是同一个容器!也大概了解了容器和Servlet容器的关系;


ServletContext的作用范围是整个应用

创建过程


先看实际继承关系

我们知道 Servlet 的生命周期包括

  • 初始化阶段 ,调用init()方法

  • 响应客户请求阶段,调用service()方法

  • 终止阶段,调用destroy()方法

Springboot默认是在第一次调用时才触发的init初始化,此时DispatcherServlet虽然已经是bean了,但是还没有成为一个可以被使用的bean;

调用过程如下:

  1. org.springframework.web.servlet.HttpServletBean#init

  2. org.springframework.web.servlet.FrameworkServlet#initServletBean

  3. org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext

cwac.setParent(rootContext);
...if (!this.refreshEventReceived) {this.onRefresh(wac);}
  1. org.springframework.web.servlet.DispatcherServlet#onRefresh

  2. org.springframework.web.servlet.DispatcherServlet#initStrategies

调用到这里 所以在Servlet初始化时,创建9大DispatcherServlet对象

     initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);

在这完成以后,可能大家就都特别熟悉doDispatch()方法了

结论

至此 从run开始到目前为止,已经创建出了一个可以正常使用的DispatcherServlet,其实这部分内容可能没多少人愿意了解,但是它是你理解整个springmvc运行的非常重要的步骤,尤其是对于父子容器、ioc容器、web上下文等的概念。
我们可以通过API直接请求我们的服务了。这里只讲了创建的流程,并没有讲解请求是如何调用的。如果有机会出下一篇,希望能把Springmvc的调用过程讲清楚。

❤❤❤

SpringMVC的工作原理(创建篇)相关推荐

  1. 【转载】SpringMVC的工作原理

    声明: 转载文章署名及贴出原文链接 -------------------------------------------- 知乎-杀戮苍生 原文地址:https://zhuanlan.zhihu.c ...

  2. SSM三大框架+SpringMVC的工作原理及其流程

    SSM三大框架+SpringMVC的工作原理及其流程 一.SSM中各层作用及关系 1.持久层:DAO层(mapper层)(属于mybatis模块)     DAO层(Mapper层):主要负责与数据库 ...

  3. springMVC 的工作原理和机制

    转载自 https://www.cnblogs.com/zbf1214/p/5265117.html 工作原理 上面的是springMVC的工作原理图: 1.客户端发出一个http请求给web服务器, ...

  4. 以太坊的工作原理 程序篇

    这篇文章主要讲解以太坊的基本原理,对技术感兴趣的朋友可以看看. 原文地址:How does Ethereum work, anyway? 简介 不管你们知不知道以太坊(Ethereum blockch ...

  5. 调试器工作原理——基础篇

    #include <stdio.h>int main(){printf("Hello, world!n");return 0;} 本文是一系列探究调试器工作原理的文章的 ...

  6. python调试器原理_调试器工作原理——基础篇

    本文是一系列探究调试器工作原理的文章的第一篇.我还不确定这个系列需要包括多少篇文章以及它们所涵盖的主题,但我打算从基础知识开始说起. 关于本文 我打算在这篇文章中介绍关于Linux下的调试器实现的主要 ...

  7. Linux调试器工作原理——基础篇

    英文原文:Eli Bendersky编译:伯乐在线-陈舸 本文是一系列探究调试器工作原理的文章的第一篇.我还不确定这个系列需要包括多少篇文章以及它们所涵盖的主题,但我打算从基础知识开始说起. 关于本文 ...

  8. 面试官问你 SpringMVC 的工作原理,你还不知道吗?

    SpringMVC的工作原理图: SpringMVC流程 1. 用户发送请求至前端控制器DispatcherServlet. 2. DispatcherServlet收到请求调用HandlerMapp ...

  9. 框架:SpringMVC的工作原理

    SpringMVC的工作原理图: SpringMVC流程 1.  用户发送请求至前端控制器DispatcherServlet. 2.  DispatcherServlet收到请求调用HandlerMa ...

最新文章

  1. python编程入门指南怎么样-python编程从入门到实践这本书怎么样
  2. 116.网络里的时延和带宽
  3. 单片机原理及其应用——单片机控制8只发光二极管交替闪烁
  4. 为什么线程池里的方法会执行两次_面试官问你java都有哪些线程池,自己是否自定义过线程池...
  5. 苏宁易购:公司改选董事 同意聘任张近东为公司名誉董事长
  6. 关于QT编译错误问题
  7. Kettle:创建资源库
  8. JAVA CLASS混淆工具:RetroGuard(已无法下载)
  9. hd计算机技术,BD和HD的区别是什么?
  10. 斗鱼封禁主播陈一发,新媒体有出路吗?
  11. matlab绘制那奎斯特曲线和bode图
  12. 清华大学 zhongguo li 计算机,2013年EI收录中国期刊名单(包括新收录的).xls
  13. 3.2-上位机与下位机的“私有协议”通信构架设计
  14. Google天气和股票API
  15. C语言练习题之标准电话号码(MOOC)
  16. 全球天气网(tianqi.com)天气预报调用插件
  17. lisp提取长方形坐标_如何利用lisp程序一次性提取CAD中点的坐标(不要点击每个点,太多了麻烦)...
  18. 在安装Centos时如何选择磁盘的分区?
  19. Bean named ‘userService‘ is expected to be of type ‘com.zkf.service.userServiecImpl‘ but[我的报错日常]
  20. X3850 X5 间歇性亮黄灯

热门文章

  1. 网页生成二维码并实现打印的两种方式
  2. 知乎高赞回答:有什么相见恨晚的学英语方法?
  3. centos7安装haroopad
  4. vue和表格和饼状图的渲染
  5. 开源轻量级的定时器调度器 | SmartTimer
  6. 正则语言(转的 大额_skylar )
  7. 《大营救》将登陆央视 高曙光李佳璇领衔主演
  8. 《疯狂72小时》点映获好评 有望成暑期档喜剧黑
  9. SAP HANA 高性能内存数据库
  10. 字符串的排列数-Java