SpringMVC的启动流程与原理

做过web项目的都知道页面连接后端程序中间是需要一个连接器来进行连接控制的。拿最常用的web容器tomcat来说,我们用tomcat搭建一个简单的web应用,就是配置好tomcat的web.xml文件,然后将后端应用打成一个war放到tomcatwebapp下就能完成对应web项目的部署。
传统的定义servlet的方式稍显有些繁琐,而且容易出错,随着web应用越来越庞大,web.xml里的内容也越来越多。这时候就有了springMVC的问世,当然struts也属于这一类框架。但是不管是从应用的广泛性还是实用性来说,目前springMVC是主流,其完美对接spring应用,以及使用spring框架集成各类中间件与其他框架也是简单易用。

SpringMVC的启动流程

Tomcat的启动以及加载Spring配置文件整个过程绕来绕去,对有些初学者来说其实不算太友好,这里就对整个SpringMVC的启动流程做了简单的阐述。
大致可以分为如下几步,其中包含了从Tomcat调用到SpringMVC,然后又从SpringMVC回调到Tomcat中Servlet的相关初始化(虽然也是SpringMVC的相关内容)。
在这里插入图片描述

Tomcat通过Servlet监听启动springioc容器

<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath*:spring-ioc.xml</param-value>
	</context-param>
    <!-- 配置监听器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

	<!-- MVC Dispatcher -->
	<servlet>
		<servlet-name>MVC Dispatcher</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>
	<servlet-mapping>
		<servlet-name>MVC Dispatcher</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

首先上springMVC简单的web.xml配置。基本上这几行配置就能完成一个简单的web应用的搭建。

指定Servlet的监听器,Tomcat在启动之后会执行器ServletContextListener监听器里的contextInitialized方法。
springMVC默认使用的是spring自己实现的servlet监听器ServletContextListener,然后通过公共参数配置指定加载的spring容器的配置文件,来完成对spring-ioc容器的加载初始化。

public void contextInitialized(ServletContextEvent event) {
		initWebApplicationContext(event.getServletContext());
	}

Tomcat初始化Servlet来完成SpringMVC容器的启动

我们都知道Servlet的生命周期,是在Tomcat启动时完成相关初始化的,而整个SpringMVC中就注册了一个Servlet,那就是DispatcherServlet,这个也是整个SpringMVC中最重要的一个类。

@Override
	public final void init() throws ServletException {

		// Set bean properties from init parameters.
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				initBeanWrapper(bw);
				bw.setPropertyValues(pvs, true);
			}
			catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}

		// Let subclasses do whatever initialization they like.
		//调用具体的子类实现初始化方法,模板方法,具体有子类实现
		initServletBean();
	}

调用到子类FrameworkServlet中的initServletBean来完成对springMVC容器的刷新。
但是在容器刷新之前,会先注册一个spring容器的监听事件,在完成容器刷新后对其进行回调。

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			// The application context id is still set to its original default value
			// -> assign a more useful id based on available information
			if (this.contextId != null) {
				wac.setId(this.contextId);
			}
			else {
				// Generate default id...
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
			}
		}

		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.setNamespace(getNamespace());
		//为当前spring上下文添加一个监听事件,保证后面SpringMVC九大组件的初始化
		wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

		// The wac environment's #initPropertySources will be called in any case when the context
		// is refreshed; do it eagerly here to ensure servlet property sources are in place for
		// use in any post-processing or initialization that occurs below prior to #refresh
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
		}

		postProcessWebApplicationContext(wac);
		applyInitializers(wac);
		wac.refresh();
	}

SpringMVC九大组件的初始化

在之前就说过了,当springMVC容器完成刷新后,会通过监听事件来回调DispatcherServlet的initStrategies方法:

	protected void initStrategies(ApplicationContext context) {
		//初始化文件上传解析器,没有默认的处理器,
		// 如果需要涉及到文件上传,需要收到注入对应的实现类
		initMultipartResolver(context);
		//初始化本地解析器,主要用来解决国际化的
		initLocaleResolver(context);
		//初始化主题解析器
		initThemeResolver(context);
		//初始化处理器映射器,主要解决根据请求uri来定位到具体的Controller的业务方法
		initHandlerMappings(context);
		//初始化处理器适配器,
		//因为事先控制器(Controller)的方法有三种,
		//1.注解@Controller;2.实现Controller接口;3.实现HttpRequestHandler接口
		// 所以当要调用到具体的业务Controller方法需要不同的适配器来处理
		initHandlerAdapters(context);
		//初始化异常处理器,常用的有统一异常处理器,如报文封装转意成错误码
		initHandlerExceptionResolvers(context);
		//初始化请求视图转化器()
		initRequestToViewNameTranslator(context);
		//初始化视图解析器
		initViewResolvers(context);
		//初始快照Map管理器,主要用来做重定向请求的参数传递
		initFlashMapManager(context);
	}

在SpringMVC框架中,基本为这九大组件设置相对应的默认处理器,可以查看springMvc报下的DispatcherServlet.properties,除了MultipartResolver文件上传的解析器,其他都有默认配置。

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
	org.springframework.web.servlet.function.support.RouterFunctionMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
	org.springframework.web.servlet.function.support.HandlerFunctionAdapter


org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

而其中组件加载大致可以分为两大类:

  • 单个对象:FlashMapManager、RequestToViewNameTranslator、ThemeResolver、MultipartResolver、LocaleResolver。

单个对象的初始化,都是先从spring容器(这里包含Spring-ioc父容器与Spring-MVC自容器)中进行查找匹配。如果容器中不存在符合条件的对应解析器,则采用默认策略,即从上面配置文件中,配置的对应解析器,如果配置了多个,则取第一个。

private void initLocaleResolver(ApplicationContext context) {
		try {
			//根据指定的beanName和对应实例类型匹配
			this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Detected " + this.localeResolver);
			}
			else if (logger.isDebugEnabled()) {
				logger.debug("Detected " + this.localeResolver.getClass().getSimpleName());
			}
		}
		catch (NoSuchBeanDefinitionException ex) {
			// We need to use the default.
			//从DispatchServlet.properties里获取对应的默认解析器  AcceptHeaderLocaleResolver
			this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME +
						"': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
			}
		}
	}

从配置文件读取到对应的权限定类名,然后利用spring容器直接创建Bean对象,如有需要自定义一些初始化工作,则利用Spring容器创建Bean的扩展性完成。如实现ApplicationContextAware接口,或者InitializingBean接口,其中ApplicationContextAware可以加拿到当前容器的上下文,做的事情就多了去了。

  • 集合对象:HandlerMapping、HandlerAdapter、HandlerExceptionResolver、ViewResolver。这四个也是整个SpringMVC九大组件中最重要的三个组件,也是运用最广泛的。准确的说HandlerMapping、HandlerAdapter、HandlerExceptionResolver这三个,因为目前都是前后端分离的项目居多,视图解析器使用就没那么多了。

若是集合类型的处理器则初始化的操作有些不同,首先是根据标志位判断是否从整个容器中查找再来匹配,或者从根据具体的BeanName和类型定向查找;

private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;

		//判断是否需要从整个spring容器中扫描获取HandlerMapping的实现类实例。默认为true
		if (this.detectAllHandlerMappings) {
			// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<>(matchingBeans.values());
				// We keep HandlerMappings in sorted order.按默认规则排序
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		else {
			//如果不允许从容器中获取HandlerMapping,则从容器中对应对应实例名为handlerMapping的bean
			try {
				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
				this.handlerMappings = Collections.singletonList(hm);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerMapping later.
			}
		}

		// Ensure we have at least one HandlerMapping, by registering
		// a default HandlerMapping if no other mappings are found.
		//如果从容器中没有找到一个HandlerMapoing,则采用默认策略
		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
						"': using default strategies from DispatcherServlet.properties");
			}
		}

		//遍历所有的HandlerMapping,是否有一个支持parseRequestPath,若有则设置当前DispatcherServlet的parseRequestPath属性为true
		for (HandlerMapping mapping : this.handlerMappings) {
			if (mapping.usesPathPatterns()) {
				this.parseRequestPath = true;
				break;
			}
		}
	}

这三个初始化策略基本都这样,默认都是根据接口名直接从容器中查找,若找不到则采用默认策略,取配置文件里的权限定类名然后完成Bean对象的创建很初始化。
到这里SpringMVC基本启动完成,可以正常对外提供服务了。

码上书
关注 关注
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringMVC底层执行流程及原理解析
08-19
SpringMVC底层执行流程及原理解析 SpringMVC作为Java开发中的一个重要框架,了解其底层执行流程及原理是非常重要的。本文将通过示例代码详细介绍SpringMVC底层执行流程及原理,并对其进行深入分析。 一、...
SpringMVC启动流程(总结)
weixin_53870150的博客
08-21 4864
SpringMVC启动流程(详细)
Spring MVC 启动流程一览
爱老婆爱生活~
03-25 1652
Spring MVC 是我们最为熟悉的框架,但他的启动流程却少有人能理清,本文将简化纷繁复杂的逻辑,对整个流程抽丝剥茧,只讲述将最重点的内容!
一文直接搞懂SpringMVC完整版教程
最新发布
2301_78772942的博客
09-02 1973
一文直接搞懂SpringMVC完整版教程
SpringMVC系列-1 使用方式和启动流程
Sheng_Q的博客
06-18 5628
当Tomcat被部署到服务器或者通过本地IDEA将项目war包通过local tomcat部署到Tomcat上后,可以通过startup.sh或者startup.bat触发Bootstrap的main方法,从而开启Tomcat容器组件的初始化和启动过程。从宏观上看,启动过程中对应着Listener-> Filter -> Servlet组件的触发流程。本文涉及的组件包括Listener和Servlet:Tomcat在构造Context实例后会触发ServletContextEvent事件
spring启动流程 (6完结) springmvc启动流程
xuguofeng2016的博客
07-26 2257
SpringMVC启动入口在SpringServletContainerInitializer类,它是ServletContainerInitializer实现类(Servlet3.0新特性)。 SpringMVC将大部分启动逻辑封装在了抽象WebApplicationInitializer中。 本文将详细分析ServletContainerInitializer、SpringServletContainerInitializer和WebApplicationInitializer的工作流程。
这一次搞懂SpringMVC原理说明
09-07
这一次深入理解SpringMVC原理,将帮助我们更好地掌握这个强大的Web框架在处理HTTP请求时的工作流程。SpringMVCSpring框架的重要组成部分,主要用于构建Web应用的Model-View-Controller架构。在本文中,我们将探讨...
SpringMvc web.xml配置实现原理过程解析
08-18
SpringMvc web.xml配置实现原理过程解析 SpringMvc 中的web.xml配置是实现原理过程解析的关键一步。在这篇文章中,我们将详细介绍SpringMvc web.xml配置实现原理过程解析的过程,包括过滤器CharacterEncodingFilter...
SpringMVC教程及实现原理
05-03
### SpringMVC教程及其实现原理 #### 一、SpringMVC概述 SpringMVCSpring框架中的一个重要组成部分,主要用于构建Web应用。它基于MVC(Model-View-Controller)设计模式,帮助开发者构建可扩展的、易于维护的Web...
SpringMVC启动流程
qq_44027353的博客
06-28 1610
SpringMVC启动流程如下。
SpringMVC启动流程
2302_77659577的博客
08-23 349
以上便是SpringMVC启动的大致流程了,后面我们再来详细探究它处理请求的流程.
面试官:说一下Spring MVC启动流程呗!
小识的博客
03-25 1196
基于XML配置的容器启动过程 我们常用的Spring MVC是基于Servlet规范实现的,所以我们先来回顾一下Servlet相关的内容。 如果我们直接用Servlet来开发web应用,只需要继承HttpServlet,实现service方法即可,HttpServlet继承自Servlet,Servlet中常用的方法如下 public interface Servlet { // 初始化,只会被调用一次,在service方法调用之前完成 void init(ServletConfig con.
SpringMVC启动流程方式分析-三种方式
nobaldnolove的博客
04-18 1942
SpringMvc启动的三种方式
SpringMVC工作流程
xiaomagezuishuai的博客
02-17 125
SpringMVC启动过程 1、用户发送请求至前端控制器DispatcherServlet 2、DispatcherServlet收到请求调用HandlerMapping处理器映射器查找Handler。 3、处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。 4、DispatcherServlet通过HandlerAdapter处理器适配器调用处理器 5、HandlerAdapter调用处理器Handler 6、Handle
Spring springmvc启动流程
踏浪归来
01-24 9016
       Spring启动是建筑在servlet容器之上的,所有web工程的初始位置就是web.xml,它配置了servlet的上下文(context)和监听器(Listener),下面就来看看web.xml里面的配置:     &lt;web-app&gt;            &lt;welcome-file-list&gt;             &lt;welcome-file...
彻底搞懂spring MVC启动和处理请求流程
weixin_36279234的博客
12-08 912
spring MVC已经使我们再熟悉不过的框架了,今天从源码的角度来剖析spring MVC的核心组件DispatchServlet是如何运转各个组件
SpringMVC(一)
m0_71774042的博客
09-01 219
springmvc是一个专门简化表述层数据接收和数据返回的框架第一层自定义web.xml的替代类,继承AbstractAnnotationConfigDispatcherServletInitializer第二层类:AbstractAnnotationConfigDispatcherServletInitializer,其继承了第三层第三层类:AbstractDispatcherServletInitializer,其继承了第四层。
写文章

热门文章

  • 三种方法解决百分之九十九的jar包冲突 15436
  • LVS负载均衡器的原理简单介绍 2656
  • SpringMVC的启动流程与原理 2565
  • linux操作系统上安装nginx 1519
  • Spring声明式调用Feign原理以及扩展应用 544

分类专栏

  • 开发经验 1篇

最新评论

  • 三种方法解决百分之九十九的jar包冲突

    曾某人啊: 请问这个问题解决了,需要重新签名吗,有相关教程吗

  • 三种方法解决百分之九十九的jar包冲突

    nik_yyyy: 用最后一种方法弄完之后,程序报错JCE cannot authenticate the provider BC;

大家在看

  • 蓝易云 - 在虚拟机Docker环境下部署Nginx的步骤。 371
  • 蓝易云 - Java编程技巧:<where>和<if>标签的组合使用示例 146

最新文章

  • 三种方法解决百分之九十九的jar包冲突
  • Spring声明式调用Feign原理以及扩展应用
  • LVS负载均衡器的原理简单介绍
2022年5篇
2021年2篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值

玻璃钢生产厂家山东主题商场美陈订购玻璃钢雕塑高度商场4月份美陈花艺青海玻璃钢博物馆雕塑济源商店玻璃钢雕塑吉林佛像玻璃钢雕塑定做价格玻璃钢雕塑原材料供应商广西玻璃钢狗雕塑四川户外商场美陈多少钱北京创意玻璃钢雕塑设计山东步行街玻璃钢雕塑生产厂家玻璃钢玻璃钢动物雕塑吕梁玻璃钢广场雕塑价格国庆节商场美陈广安玻璃钢雕塑定制玻璃钢花槽定做方形花盆安宁玻璃钢烤漆雕塑厂家苏州玻璃钢卡通雕塑厂家直销玻璃钢雕塑原装正品玻璃钢雕塑技术说明惠安玻璃钢园林雕塑中式玻璃钢雕塑一般多少钱六一儿童节商场美陈 ppt无锡商场美陈宁波玻璃钢飞马雕塑洛阳玻璃钢彩绘雕塑定制玻璃钢运动人物雕塑设计好的玻璃钢花盆批发玻璃钢雕塑施工组织方案地下商场美陈方案香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化