Tomcat原理系列之四:Tomat如何启动spring

  • 熟悉的web.xml
    • ContextLoaderListener
  • Tomcat的初始化StandardContext.startInternal()
    • 1.Tomcat对web.xml的加载.
    • 2.Tomcat 执行了listener.
    • 3.ContextLoaderListener.contextInitialized(event) spring的初始化.

在springboot盛行的今天,你是否还记得那,在xml文件中配置各种servlet, filter的日子。是否还记得那Tomat+spring+springmvc配置的组合。还有那熟悉的web.xml文件。不知你当时是否有过为何如此配置的疑惑?你又是否已经解惑。

不要带着疑惑让他们远去。我们一起回顾

熟悉的web.xml

ContextLoaderListener

为了使用spring我们常见在web.xml中做这样的配置. 配置一个ContextLoaderListener监听器。这个监听器是如何把Tomcat与spring关联的呢?

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">  <filter>  <filter-name>characterEncodingFilter</filter-name>  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  <init-param>  <param-name>encoding</param-name>  <param-value>UTF-8</param-value>  </init-param>  <init-param>  <param-name>forceEncoding</param-name>  <param-value>true</param-value>  </init-param>  </filter>  <filter-mapping>  <filter-name>characterEncodingFilter</filter-name>  <url-pattern>/*</url-pattern>  </filter-mapping>  <!--启用spring--><listener>  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  </listener>  <!--2、部署applicationContext的xml文件--> <context-param>  <param-name>contextConfigLocation</param-name>  <param-value>classpath:spring/applicationContext.xml</param-value>  </context-param>  <!--3、启用springmvc--> <servlet>  <servlet-name>DispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  <init-param>  <param-name>contextConfigLocation</param-name>  <param-value>classpath:spring/dispatcher-servlet.xml</param-value>  </init-param>  <load-on-startup>1</load-on-startup><!--是启动顺序,让这个Servlet随Servletp容器一起启动。-->  </servlet>  <servlet-mapping>  <servlet-name>DispatcherServlet</servlet-name>  <url-pattern>/</url-pattern> <!--会拦截URL中带“/”的请求。-->  </servlet-mapping>  <welcome-file-list><!--指定欢迎页面-->  <welcome-file>login.html</welcome-file>  </welcome-file-list>  <error-page> <!--当系统出现404错误,跳转到页面nopage.html-->  <error-code>404</error-code>  <location>/nopage.html</location>  </error-page>  <error-page> <!--当系统出现java.lang.NullPointerException,跳转到页面error.html-->  <exception-type>java.lang.NullPointerException</exception-type>  <location>/error.html</location>  </error-page>  <session-config><!--会话超时配置,单位分钟-->  <session-timeout>360</session-timeout>  </session-config>
</web-app>

Tomcat的初始化StandardContext.startInternal()

1.Tomcat对web.xml的加载.

要解开Tomcat与spring的关系,我们首先应该先搞懂,Tomcat是在什么位置,如何加载的web.xml文件。

说到这个问题,我们不得不提一下Tomcat启动的三大主线中的start()主线。

Tomcat 层级调用组件的start()方法,执行到StandardContext.startInternal() 时, 在startInternal()方法中调用fireLifecycleEvent()发布一个"configure_start" 事件.

 public static final String CONFIGURE_START_EVENT = "configure_start";// Notify our interested LifecycleListenersfireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);//响应configure_start事件protected void fireLifecycleEvent(String type, Object data) {LifecycleEvent event = new LifecycleEvent(this, type, data);for (LifecycleListener listener : lifecycleListeners) {listener.lifecycleEvent(event);}}

web.xml之旅就此开始。

在众多监听器中,有一个ContextConfig监听器,在监听到"configure_start" 事件后, 会执行configureStart()方法. 在configureStart()方法中执行webConfig()开始web.xml解析.

lifecycleEvent()==》if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {configureStart();//} else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {beforeStart();} else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {configureStart()==》if (log.isDebugEnabled()) {log.debug(sm.getString("contextConfig.start"));}if (log.isDebugEnabled()) {log.debug(sm.getString("contextConfig.xmlSettings",context.getName(),Boolean.valueOf(context.getXmlValidation()),Boolean.valueOf(context.getXmlNamespaceAware())));}webConfig();

webConfig:就是Tomcat加载解析web.xml的地方
方法中注释标注了1,2…步骤,详细讲解web.xml加载,解析的过程.讲的很详细,我这里不一一讲了,强烈建议大家去看看源码。
这里主要提一个两个重要的点

parseWebXml(contextWebXml, webXml, false)方法:
这个方法中有一个Digester工具,在Tomcat加载server.xml配置文件的时候就是使用了此工具,解析原理异曲同工。 此处使用WebRuleSet规则,将web.xml文件中的配置读取出来设置到webXml对象中去.
configureContext(StandardContext context)方法
将web.xml文件解析出来的各个组件设置到标准servlet上下文StandardContext中去。 其中就包括我们的filter ,servlet, listener

parseWebXml()===>Digester digester;WebRuleSet ruleSet;if (fragment) {digester = webFragmentDigester;ruleSet = webFragmentRuleSet;} else {digester = webDigester;ruleSet = webRuleSet;}configure()===>private void configureContext(WebXml webxml) {for (FilterDef filter : webxml.getFilters().values()) {if (filter.getAsyncSupported() == null) {filter.setAsyncSupported("false");}context.addFilterDef(filter);}for (FilterMap filterMap : webxml.getFilterMappings()) {context.addFilterMap(filterMap);}....for (ServletDef servlet : webxml.getServlets().values()) {Wrapper wrapper = context.createWrapper();}...for (String listener : webxml.getListeners()) {context.addApplicationListener(listener);}}

至此StandardContext已经有了我们配置的listener,fitler,servlet

2.Tomcat 执行了listener.

在加载并将filter,servlet,listener设置到contxt中后,接下来就是执行了。

还是StandardContext.startInternal()方法, 在方法的下半部分按顺序有如下操作:
listenerStart() 启动所有的listener.
filterStart() 启动所有的filter
loadOnStartup(findChildren()) 启动所持有的servlet
listenerStart() 启动众多listener,其中就包括我们配置的ContextLoaderListener监听器。

             // Configure and call application event listenersif (ok) {if (!listenerStart()) {log.error(sm.getString("standardContext.listenerFail"));ok = false;}}...// Configure and call application filtersif (ok) {if (!filterStart()) {log.error(sm.getString("standardContext.filterFail"));ok = false;}} ...// Load and initialize all "load on startup" servletsif (ok) {if (!loadOnStartup(findChildren())){log.error(sm.getString("standardContext.servletFail"));ok = false;}}

3.ContextLoaderListener.contextInitialized(event) spring的初始化.

listenerStart() 方法其实就是调用Listener的contextInitialized()方法

 public boolean listenerStart() {...for (int i = 0; i < instances.length; i++) {if (!(instances[i] instanceof ServletContextListener))continue;ServletContextListener listener =(ServletContextListener) instances[i];try {fireContainerEvent("beforeContextInitialized", listener);if (noPluggabilityListeners.contains(listener)) {listener.contextInitialized(tldEvent);} else {//执行listener的初始。传递ServletContextEvent参数listener.contextInitialized(event);//}fireContainerEvent("afterContextInitialized", listener);} catch (Throwable t) {ExceptionUtils.handleThrowable(t);fireContainerEvent("afterContextInitialized", listener);getLogger().error(sm.getString("standardContext.listenerStart",instances[i].getClass().getName()), t);ok = false;}}...}

来到ContextLoaderListener.contextInitialized(ServletContextEvent event)方法中,开始初始化web应用下的IO容器。

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

initWebApplicationContext方法中,调用了createWebApplicationContext方法来构建一个上下文类,createWebApplicationContext方法中首先调用determineContextClass()来判断上下文类型决定创建哪种上下文, 通常会使用默认策略,根据ContextLoader.properties文件中配置的WebApplicationContext值创建上下文XmlWebApplicationContext对象
至此web容器实例就创建出来了

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

紧接着调用configureAndRefreshWebApplicationContext()方法来初始化bean.就开始了spring的著名的初始化方法refresh()

 public void refresh() throws BeansException, IllegalStateException {synchronized(this.startupShutdownMonitor) {this.prepareRefresh();ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();this.prepareBeanFactory(beanFactory);try {this.postProcessBeanFactory(beanFactory);this.invokeBeanFactoryPostProcessors(beanFactory);this.registerBeanPostProcessors(beanFactory);this.initMessageSource();this.initApplicationEventMulticaster();this.onRefresh();this.registerListeners();this.finishBeanFactoryInitialization(beanFactory);this.finishRefresh();} catch (BeansException var9) {if (this.logger.isWarnEnabled()) {this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);}this.destroyBeans();this.cancelRefresh(var9);throw var9;} finally {this.resetCommonCaches();}}}

总结:ContextLoaderListener通过实现ServletContextListener接口,继承ContextLoader加载器。 把Tomcat与spring连接到了一起。

看懂了Tomcat与spring的关系, 想想配置的DispatcherServlet。其中原理是否有门道了

Spring分别提供了用于启动WebApplicationContext的Servlet和Web容器监听器:
org.springframework.web.context.ContextLoaderServlet;
org.springframework.web.context.ContextLoaderListener.
ContextLoaderListener,ContextLoaderServlet 其实区别不大。流程是一样的

Tomcat原理系列之四:Tomat如何启动spring(加载web.xml)相关推荐

  1. spring加载ApplicationContext.xml的四种方式

    spring加载ApplicationContext.xml的四种方式 spring 中加载xml配置文件的方式,好像有4种, xml是最常见的spring 应用系统配置源.Spring中的几种容器都 ...

  2. spring容器扩展功能之一:spring加载ApplicationContext.xml的四种方式

    容器加载Bean的常见两个类ApplicationContext和BeanFactory, 一.首先,看看spring中加载配置在xml中的Bean对象到容器 spring 中加载xml配置文件的方式 ...

  3. 【网址收藏】dubbo特新概念及特性、环境搭建、dubbo-monitor安装、rpc原理以及dubbo原理:框架设计、启动解析加载配置信息、服务暴露、服务引用及调用

    https://blog.csdn.net/qq_41157588/article/details/106737191

  4. spring加载application.xml异常

    2019独角兽企业重金招聘Python工程师标准>>> Caused by: org.springframework.beans.factory.xml.XmlBeanDefinit ...

  5. Spring mvc 启动配置文件加载两遍问题

    问题描述 在使用spring mvc 启动的时候,用到了一个在程序启动时加载的配置方法init-method="initLoad",并启动多线程来做数据同步,但是在程序启动之后发现 ...

  6. [Zookeeper-3.6.2源码解析系列]-13- 法定人对象QuorumPeer启动之加载磁盘快照与事务日志

    目录 13- 法定人对象QuorumPeer启动之加载磁盘快照与事务日志 13.1 简介 12.3 恢复本地数据到内存的模板方法 13.3 Zookeeper数据库管理加载磁盘快照的核心方法 13.3 ...

  7. java类spring加载_spring的加载机制?

    1,今天面试官问我spring的加载机制有哪些---这么"抽象"的问题作为一个十多年经验的自己写过MVC,IOC,ORM, 等各种中间件小框架的开发人员也回答不出来~ 确切的说是无 ...

  8. Spring加载properties文件的两种方式

    2019独角兽企业重金招聘Python工程师标准>>> 在项目中如果有些参数经常需要修改,或者后期可能需要修改,那我们最好把这些参数放到properties文件中,源代码中读取pro ...

  9. Spring加载流程源码

    一.从AbstractApplicationContext的体系说起 第一,从类结构设计上看, 围绕着是否需要Refresh容器衍生出两个抽象类: GenericApplicationContext: ...

最新文章

  1. R语言dplyr包的mutate函数将列添加到dataframe中或者修改现有的数据列:使用na_if()函数将0值替换为NA值、负收入替换为NA值
  2. SIEM已死?标题党!
  3. 微信小程序code 换取 session_key
  4. Linux学习笔记01
  5. git object 很大_这才是真正的Git——Git内部原理
  6. java tostring方法_Java虚拟机如执行方法调用的(二)?
  7. jq(jquery)之点击隐藏段落
  8. 计算机网络实验报告校园网,校园网规划与设计实验报告.docx
  9. Zabbix动态监控磁盘I/O
  10. MySQL 事务 MVCC 版本链
  11. xv6 System Call
  12. c语言餐桌游戏,教会你这十款酒桌游戏,让你在朋友圈稳站“C”位!
  13. Java对接ChinaPay提现(公私钥方式)
  14. kindle电子书转换成pdf azw转pdf
  15. 苹果电脑如何双开微信
  16. android audiomixer,Android多媒体:AudioMixer
  17. gmx_MMPBSA.py的安装及使用--只翻译部分内容,具体可参考官方文档(https://valdes-tresanco-ms.github.io/gmx_MMPBSA/dev/)
  18. 2023年基建工程(设计规划施工)经验分享,超多干货
  19. win10计算机亮度在哪里调,win10电脑怎么调亮度
  20. threejs:流光效果封装

热门文章

  1. 0124:镂空三角形(C++)
  2. 管家婆软件B/S系列打印管理器报错解决
  3. Win10,Cuda 11.1 下载与安装
  4. C#实现力扣双周赛算法题:以组为单位订音乐会的门票订购
  5. mac 黑屏后不能启动系统的解决方案
  6. 如何设置U盘存储使其存储超过4G的文件
  7. ICOM IC-F26 使用MDC信令“蛙叫”及SQL设置
  8. POJ 2429 GCD LCM Inverse (整数分解,由gcd+lcm求a,b)
  9. Java项目:物流快递管理系统(java+SSM+jsp+mysql)
  10. SSL/TLS加密证书生成(一)