SpringMVC的工作原理(创建篇)

概述

目前 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 it
			wac = 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的调用过程讲清楚。

❤❤❤

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值