Tomcat原理系列之四:Tomat如何启动spring(加载web.xml)
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)相关推荐
- spring加载ApplicationContext.xml的四种方式
spring加载ApplicationContext.xml的四种方式 spring 中加载xml配置文件的方式,好像有4种, xml是最常见的spring 应用系统配置源.Spring中的几种容器都 ...
- spring容器扩展功能之一:spring加载ApplicationContext.xml的四种方式
容器加载Bean的常见两个类ApplicationContext和BeanFactory, 一.首先,看看spring中加载配置在xml中的Bean对象到容器 spring 中加载xml配置文件的方式 ...
- 【网址收藏】dubbo特新概念及特性、环境搭建、dubbo-monitor安装、rpc原理以及dubbo原理:框架设计、启动解析加载配置信息、服务暴露、服务引用及调用
https://blog.csdn.net/qq_41157588/article/details/106737191
- spring加载application.xml异常
2019独角兽企业重金招聘Python工程师标准>>> Caused by: org.springframework.beans.factory.xml.XmlBeanDefinit ...
- Spring mvc 启动配置文件加载两遍问题
问题描述 在使用spring mvc 启动的时候,用到了一个在程序启动时加载的配置方法init-method="initLoad",并启动多线程来做数据同步,但是在程序启动之后发现 ...
- [Zookeeper-3.6.2源码解析系列]-13- 法定人对象QuorumPeer启动之加载磁盘快照与事务日志
目录 13- 法定人对象QuorumPeer启动之加载磁盘快照与事务日志 13.1 简介 12.3 恢复本地数据到内存的模板方法 13.3 Zookeeper数据库管理加载磁盘快照的核心方法 13.3 ...
- java类spring加载_spring的加载机制?
1,今天面试官问我spring的加载机制有哪些---这么"抽象"的问题作为一个十多年经验的自己写过MVC,IOC,ORM, 等各种中间件小框架的开发人员也回答不出来~ 确切的说是无 ...
- Spring加载properties文件的两种方式
2019独角兽企业重金招聘Python工程师标准>>> 在项目中如果有些参数经常需要修改,或者后期可能需要修改,那我们最好把这些参数放到properties文件中,源代码中读取pro ...
- Spring加载流程源码
一.从AbstractApplicationContext的体系说起 第一,从类结构设计上看, 围绕着是否需要Refresh容器衍生出两个抽象类: GenericApplicationContext: ...
最新文章
- R语言dplyr包的mutate函数将列添加到dataframe中或者修改现有的数据列:使用na_if()函数将0值替换为NA值、负收入替换为NA值
- SIEM已死?标题党!
- 微信小程序code 换取 session_key
- Linux学习笔记01
- git object 很大_这才是真正的Git——Git内部原理
- java tostring方法_Java虚拟机如执行方法调用的(二)?
- jq(jquery)之点击隐藏段落
- 计算机网络实验报告校园网,校园网规划与设计实验报告.docx
- Zabbix动态监控磁盘I/O
- MySQL 事务 MVCC 版本链
- xv6 System Call
- c语言餐桌游戏,教会你这十款酒桌游戏,让你在朋友圈稳站“C”位!
- Java对接ChinaPay提现(公私钥方式)
- kindle电子书转换成pdf azw转pdf
- 苹果电脑如何双开微信
- android audiomixer,Android多媒体:AudioMixer
- gmx_MMPBSA.py的安装及使用--只翻译部分内容,具体可参考官方文档(https://valdes-tresanco-ms.github.io/gmx_MMPBSA/dev/)
- 2023年基建工程(设计规划施工)经验分享,超多干货
- win10计算机亮度在哪里调,win10电脑怎么调亮度
- threejs:流光效果封装
热门文章
- 0124:镂空三角形(C++)
- 管家婆软件B/S系列打印管理器报错解决
- Win10,Cuda 11.1 下载与安装
- C#实现力扣双周赛算法题:以组为单位订音乐会的门票订购
- mac 黑屏后不能启动系统的解决方案
- 如何设置U盘存储使其存储超过4G的文件
- ICOM IC-F26 使用MDC信令“蛙叫”及SQL设置
- POJ 2429 GCD LCM Inverse (整数分解,由gcd+lcm求a,b)
- Java项目:物流快递管理系统(java+SSM+jsp+mysql)
- SSL/TLS加密证书生成(一)