热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SpringMVC的启动流程与原理

做过web项目的都知道页面连接后端程序中间是需要一个连接器来进行连接控制的。拿最常用的web容器tomcat来说,我们用tomcat搭建一个简单的web应用ÿ

做过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容器

contextConfigLocationclasspath*:spring-ioc.xmlorg.springframework.web.context.ContextLoaderListenerMVC Dispatcherorg.springframework.web.servlet.DispatcherServletcontextConfigLocationclasspath:/spring-mvc.xml1MVC Dispatcher/

首先上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中最重要的一个类。

@Overridepublic 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 informationif (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 #refreshConfigurableEnvironment 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.AcceptHeaderLocaleResolverorg.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolverorg.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\org.springframework.web.servlet.function.support.RouterFunctionMappingorg.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.HandlerFunctionAdapterorg.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.DefaultHandlerExceptionResolverorg.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslatororg.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolverorg.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里获取对应的默认解析器 AcceptHeaderLocaleResolverthis.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 &#61; null;//判断是否需要从整个spring容器中扫描获取HandlerMapping的实现类实例。默认为trueif (this.detectAllHandlerMappings) {// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.Map<String, HandlerMapping> matchingBeans &#61;BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerMappings &#61; new ArrayList<>(matchingBeans.values());// We keep HandlerMappings in sorted order.按默认规则排序AnnotationAwareOrderComparator.sort(this.handlerMappings);}}else {//如果不允许从容器中获取HandlerMapping&#xff0c;则从容器中对应对应实例名为handlerMapping的beantry {HandlerMapping hm &#61; context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);this.handlerMappings &#61; Collections.singletonList(hm);}catch (NoSuchBeanDefinitionException ex) {// Ignore, we&#39;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&#xff0c;则采用默认策略if (this.handlerMappings &#61;&#61; null) {this.handlerMappings &#61; getDefaultStrategies(context, HandlerMapping.class);if (logger.isTraceEnabled()) {logger.trace("No HandlerMappings declared for servlet &#39;" &#43; getServletName() &#43;"&#39;: using default strategies from DispatcherServlet.properties");}}//遍历所有的HandlerMapping&#xff0c;是否有一个支持parseRequestPath&#xff0c;若有则设置当前DispatcherServlet的parseRequestPath属性为truefor (HandlerMapping mapping : this.handlerMappings) {if (mapping.usesPathPatterns()) {this.parseRequestPath &#61; true;break;}}}

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


推荐阅读
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 怀疑是每次都在新建文件,具体代码如下 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • iOS超签签名服务器搭建及其优劣势
    本文介绍了搭建iOS超签签名服务器的原因和优势,包括不掉签、用户可以直接安装不需要信任、体验好等。同时也提到了超签的劣势,即一个证书只能安装100个,成本较高。文章还详细介绍了超签的实现原理,包括用户请求服务器安装mobileconfig文件、服务器调用苹果接口添加udid等步骤。最后,还提到了生成mobileconfig文件和导出AppleWorldwideDeveloperRelationsCertificationAuthority证书的方法。 ... [详细]
  • 本文讨论了如何使用Web.Config进行自定义配置节的配置转换。作者提到,他将msbuild设置为详细模式,但转换却忽略了带有替换转换的自定义部分的存在。 ... [详细]
  • Django + Ansible 主机管理(有源码)
    本文给大家介绍如何利用DjangoAnsible进行Web项目管理。Django介绍一个可以使Web开发工作愉快并且高效的Web开发框架,能够以最小的代价构建和维护高 ... [详细]
author-avatar
春哥在奋斗_
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有