Spring中ContextLoaderListener監(jiān)聽(tīng)詳解
前言
web.xml配置文件的部分,方便下面的理解:
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:Resource/SpringConf.xml</param-value> </context-param> <servlet> <servlet-name>springweb</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/springweb.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springweb</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
一、SpringMVC總覽
SpringMVC啟動(dòng)時(shí)會(huì)啟動(dòng)WebApplicationContext類型的容器,并且會(huì)調(diào)用之前分析的refresh方法。
還是從原始的SpringMVC開(kāi)始,啟動(dòng)方式有三種,配置文件中配置ContextLoadServlet,ContextLoaderListener、ContextLoaderPlugin。
不論哪種方式啟動(dòng)都大致相同。主要包括兩部分:
1、ContextLoaderListener監(jiān)聽(tīng)
初始化Spring容器,WebApplicationContext類型,并且執(zhí)行refresh方法
2、DispatcherServlet(一個(gè)特殊的Servlet,如上面的配置web.xml中進(jìn)行配置了)
1)、DispatcherServlet像普通Servlet一樣,init中啟動(dòng)九大件為服務(wù)請(qǐng)求做準(zhǔn)備
HttpServletBean#init => DispatcherServlet#onRefresh => DispatcherServlet#initStrategies(初始化九大件)
2)、當(dāng)上面初始化完成后,則請(qǐng)求進(jìn)入后攔截轉(zhuǎn)發(fā)到DispatcherServlet
DispatcherServlet#doService=> DispatcherServlet#doDispatch(執(zhí)行MVC流程)
二、ContextLoaderListener的contextInitialized監(jiān)聽(tīng)事件
當(dāng)Web容器啟動(dòng)時(shí),觸發(fā)ContextLoaderListener的contextInitialized方法:
@Override public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext()); }
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { // 省略部分日志和try catch代碼 long startTime = System.currentTimeMillis(); if (this.context == null) { this.context = createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> // determine parent for root web application context, if any. ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); } configureAndRefreshWebApplicationContext(cwac, servletContext); } } servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); } return this.context; }
這里需要考慮容器是否其他地方已經(jīng)創(chuàng)建了,但是不論是否創(chuàng)建。整個(gè)Spring MVC啟動(dòng)過(guò)程需要經(jīng)歷幾個(gè)步驟:
1、createWebApplicationContext(創(chuàng)建Web類型的ApplicationContext)
2、configureAndRefreshWebApplicationContext(主要是refresh方法執(zhí)行)
3、將初始化的WebApplicationContext容器,用key為ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE加載到ServletContext容器中
創(chuàng)建Web類型的ApplicationContext
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { Class<?> contextClass = determineContextClass(sc); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("省略"); } return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); }
先判斷啟動(dòng)的WebApplicationContext的類型Class,再使用反射初始化。主要是看啟動(dòng)的Class是怎么確定的。
protected Class<?> determineContextClass(ServletContext servletContext) { String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); if (contextClassName != null) { // 省略try catch代碼 return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } else { contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); // 省略try catch代碼 return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } }
先看啟動(dòng)Web時(shí)是否有進(jìn)行配置Class,否則就在默認(rèn)的配置策略中進(jìn)行獲取。詳細(xì)可以參見(jiàn):Spring中的策略模式簡(jiǎn)單實(shí)現(xiàn)與使用分析
從配置文件ContextLoader.properties中獲取到的類型為(所以如果沒(méi)有配置默認(rèn)啟動(dòng)XmlWebApplicationContext,Spring Boot不會(huì)啟動(dòng)該類型,但是SpringMVC沒(méi)有太大差異):
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
執(zhí)行refresh方法
不管怎么樣都回執(zhí)行configureAndRefreshWebApplicationContext方法,以啟動(dòng)Spring 容器,也就是之前一直分析refresh方法。
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { // 設(shè)置ConfigurableWebApplicationContext的容器Id 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 String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); if (idParam != null) { wac.setId(idParam); } else { // Generate default id... wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath())); } } // 設(shè)置Web容器上下文,設(shè)置到Spring容器中 wac.setServletContext(sc); String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM); // 配置location,如果配置的話,則refresh方法時(shí)會(huì)調(diào)用Resource加載,解析,初始化 if (configLocationParam != null) { wac.setConfigLocation(configLocationParam); } // 設(shè)置Environment,refresh時(shí)也分析過(guò),如果沒(méi)有則會(huì)從java.lang.System中 // 獲取數(shù)據(jù)初始化StandardEnvironment或其子類 ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(sc, null); } // 初始化自定義的容器,如果自己配置的話(我們可以自定義實(shí)現(xiàn)ServletContextListener) customizeContext(sc, wac); // 調(diào)用refresh方法 wac.refresh(); }
1、設(shè)置WebApplicationContext的Id
2、設(shè)置Web容器上下文,設(shè)置到Spring容器中
3、配置location(比如上面配置中的contextConfigLocation,則refresh方法時(shí)會(huì)調(diào)用Resource加載,解析,初始化)
4、初始化自定義的監(jiān)聽(tīng)
5、最重要的一步,啟動(dòng)Spring容器(可以從這里開(kāi)始看:SpringIoc源碼(五)- ApplicationContext(一)- 結(jié)構(gòu)梳理)
到此這篇關(guān)于Spring中ContextLoaderListener監(jiān)聽(tīng)詳解的文章就介紹到這了,更多相關(guān)ContextLoaderListener監(jiān)聽(tīng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決tk.mybatis中寫(xiě)自定義的mapper的問(wèn)題
這篇文章主要介紹了使用tk.mybatis中寫(xiě)自定義的mapper的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06Java實(shí)現(xiàn)求解一元n次多項(xiàng)式的方法示例
這篇文章主要介紹了Java實(shí)現(xiàn)求解一元n次多項(xiàng)式的方法,涉及java高斯消元法處理矩陣運(yùn)算解多項(xiàng)式的相關(guān)操作技巧,需要的朋友可以參考下2018-01-01SpringBoot使用minio進(jìn)行文件管理的流程步驟
MinIO 是一個(gè)高性能的對(duì)象存儲(chǔ)系統(tǒng),兼容 Amazon S3 API,該軟件設(shè)計(jì)用于處理非結(jié)構(gòu)化數(shù)據(jù),如圖片、視頻、日志文件以及備份數(shù)據(jù)等,本文給大家介紹了SpringBoot使用minio進(jìn)行文件管理的流程步驟,需要的朋友可以參考下2025-01-01java 數(shù)值類型分秒時(shí)間格式化的實(shí)例代碼
這篇文章主要介紹了java 數(shù)值類型分秒時(shí)間格式化的實(shí)例代碼的相關(guān)資料,將秒或分鐘的值轉(zhuǎn)換為xx天xx小時(shí)xx分鐘xx秒 如果 “xx” 為0 自動(dòng)缺省,需要的朋友可以參考下2017-07-07SpringBoot 使用 FTP 操作文件的過(guò)程(刪除、上傳、下載文件)
這篇文章主要介紹了SpringBoot 使用 FTP 操作文件,主要包括配置ftp服務(wù)器,上傳、刪除、下載文件操作,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12IDEA最新激活碼2021(IDEA2020.3.2最新永久激活方法)
這篇文章主要介紹了IDEA最新激活碼2021(IDEA2020.3.2最新永久激活方法),本文通過(guò)實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12