這篇文章主要為大家展示了“Spring MVC啟動(dòng)過(guò)程的示例分析”,內(nèi)容簡(jiǎn)而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Spring MVC啟動(dòng)過(guò)程的示例分析”這篇文章吧。
一、前置知識(shí)
大家都知道,我們?cè)谑褂胹pring mvc時(shí)通常會(huì)在 web.xml
文件中做如下配置:
web.xml
<?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"> <!-- 上下文參數(shù),在監(jiān)聽(tīng)器中被使用 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:applicationContext.xml </param-value> </context-param> <!-- 監(jiān)聽(tīng)器配置 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 前端控制器配置 --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
上面的配置總結(jié)起來(lái)有幾點(diǎn)內(nèi)容,分別是:DispatcherServlet
當(dāng)我們將spring mvc應(yīng)用部署到tomcat時(shí),當(dāng)你不配置任何的 context-param
和 listener
參數(shù),只配置一個(gè) DispatcherServlet
時(shí),那么tomcat在啟動(dòng)的時(shí)候是不會(huì)初始化spring web上下文的,換句話說(shuō),tomcat是不會(huì)初始化spring框架的,因?yàn)槟悴](méi)有告訴它們spring的配置文件放在什么地方,以及怎么去加載。所以 listener
監(jiān)聽(tīng)器幫了我們這個(gè)忙,那么為什么配置監(jiān)聽(tīng)器之后就可以告訴tomcat怎么去加載呢?因?yàn)?listener
是實(shí)現(xiàn)了servlet技術(shù)規(guī)范的監(jiān)聽(tīng)器組件,tomcat在啟動(dòng)時(shí)會(huì)先加載 web.xml
中是否有servlet監(jiān)聽(tīng)器存在,有則啟動(dòng)它們。 ContextLoaderListener
是spring框架對(duì)servlet監(jiān)聽(tīng)器的一個(gè)封裝,本質(zhì)上還是一個(gè)servlet監(jiān)聽(tīng)器,所以會(huì)被執(zhí)行,但由于 ContextLoaderListener
源碼中是基于 contextConfigLocation
和 contextClass
兩個(gè)配置參數(shù)去加載相應(yīng)配置的,因此就有了我們配置的 context-param
參數(shù)了, servlet
標(biāo)簽里的初始化參數(shù)也是同樣的道理,即告訴web服務(wù)器在啟動(dòng)的同時(shí)把spring web上下文( WebApplicationContext
)也給初始化了。
上面講了下tomcat加載spring mvc應(yīng)用的大致流程,接下來(lái)將從源碼入手分析啟動(dòng)原理。
二、Spring MVC web 上下文啟動(dòng)源碼分析
假設(shè)現(xiàn)在我們把上面 web.xml
文件中的 <load-on-startup>1</load-on-startup>
給去掉,那么默認(rèn)tomcat啟動(dòng)時(shí)只會(huì)初始化spring web上下文,也就是說(shuō)只會(huì)加載到 applicationContext.xml
這個(gè)文件,對(duì)于 applicationContext-mvc.xml
這個(gè)配置文件是加載不到的, <load-on-startup>1</load-on-startup>
的意思就是讓 DispatcherServlet
延遲到使用的時(shí)候( 也就是處理請(qǐng)求的時(shí)候
)再做初始化。
我們已經(jīng)知道spring web是基于 servlet
標(biāo)準(zhǔn)去封裝的,那么很明顯,servlet怎么初始化, WebApplicationContext
web上下文就應(yīng)該怎么初始化。我們先看看 ContextLoaderListener
的源碼是怎樣的。
public class ContextLoaderListener extends ContextLoader implements ServletContextListener { // 初始化方法 @Override public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext()); } // 銷毀方法 @Override public void contextDestroyed(ServletContextEvent event) { closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); } }
ContextLoaderListener
類實(shí)現(xiàn)了 ServletContextListener
,本質(zhì)上是一個(gè)servlet監(jiān)聽(tīng)器,tomcat將會(huì)優(yōu)先加載servlet監(jiān)聽(tīng)器組件,并調(diào)用 contextInitialized
方法,在 contextInitialized
方法中調(diào)用 initWebApplicationContext
方法初始化Spring web上下文,看到這煥然大悟,原來(lái)Spring mvc的入口就在這里,哈哈~~~趕緊跟進(jìn)去 initWebApplicationContext
方法看看吧!
initWebApplicationContext()
方法:
// 創(chuàng)建web上下文,默認(rèn)是XmlWebApplicationContext if (this.context == null) { this.context = createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; // 如果該容器還沒(méi)有刷新過(guò) if (!cwac.isActive()) { if (cwac.getParent() == null) { ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); } // 配置并刷新容器 configureAndRefreshWebApplicationContext(cwac, servletContext); } }
上面的方法只做了兩件事:
1、如果spring web容器還沒(méi)有創(chuàng)建,那么就創(chuàng)建一個(gè)全新的spring web容器,并且該容器為root根容器,下面第三節(jié)講到的servlet spring web容器是在此根容器上創(chuàng)建起來(lái)的
2、配置并刷新容器
上面代碼注釋說(shuō)到默認(rèn)創(chuàng)建的上下文容器是 XmlWebApplicationContext
,為什么不是其他web上下文呢?為啥不是下面上下文的任何一種呢?
我們可以跟進(jìn)去 createWebApplicationContext
后就可以發(fā)現(xiàn)默認(rèn)是從一個(gè)叫 ContextLoader.properties
文件加載配置的,該文件的內(nèi)容為:
復(fù)制代碼 代碼如下:
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
具體實(shí)現(xiàn)為:
protected Class<?> determineContextClass(ServletContext servletContext) { // 自定義上下文,否則就默認(rèn)創(chuàng)建XmlWebApplicationContext String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); if (contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load custom context class [" + contextClassName + "]", ex); } } else { // 從屬性文件中加載類名,也就是org.springframework.web.context.support.XmlWebApplicationContext contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load default context class [" + contextClassName + "]", ex); } } }
上面可以看出其實(shí)我們也可以自定義spring web的上下文的,那么怎么去指定我們自定義的上下文呢?答案是通過(guò)在 web.xml
中指定 contextClass
參數(shù),因此第一小結(jié)結(jié)尾時(shí)說(shuō) contextClass
參數(shù)和 contextConfigLocation
很重要~~至于 contextConfigLocation
參數(shù),我們跟進(jìn) configureAndRefreshWebApplicationContext
即可看到,如下圖:
總結(jié):
spring mvc啟動(dòng)流程大致就是從一個(gè)叫 ContextLoaderListener
開(kāi)始的,它是一個(gè)servlet監(jiān)聽(tīng)器,能夠被web容器發(fā)現(xiàn)并加載,初始化監(jiān)聽(tīng)器 ContextLoaderListener
之后,接著就是根據(jù)配置如 contextConfigLocation
和 contextClass
創(chuàng)建web容器了,如果你不指定 contextClass
參數(shù)值,則默認(rèn)創(chuàng)建的spring web容器類型為 XmlWebApplicationContext
,最后一步就是根據(jù)你配置的 contextConfigLocation
文件路徑去配置并刷新容器了。
三、 DispatcherServlet 控制器的初始化
好了,上面我們簡(jiǎn)單地分析了Spring mvc容器初始化的源碼,我們永遠(yuǎn)不會(huì)忘記,我們默認(rèn)創(chuàng)建的容器類型為 XmlWebApplicationContext
,當(dāng)然我們也不會(huì)忘記,在 web.xml
中,我們還有一個(gè)重要的配置,那就是 DispatcherServlet
。下面我們就來(lái)分析下 DispatcherServlet
的初始化過(guò)程。
DispatcherServlet
,就是一個(gè)servlet,一個(gè)用來(lái)處理request請(qǐng)求的servlet,它是spring mvc的核心,所有的請(qǐng)求都經(jīng)過(guò)它,并由它指定后續(xù)操作該怎么執(zhí)行,咋一看像一扇門,因此我管它叫“閘門”。在我們繼續(xù)之前,我們應(yīng)該共同遵守一個(gè)常識(shí),那就是-------無(wú)論是監(jiān)聽(tīng)器還是servlet,都是servlet規(guī)范組件,web服務(wù)器都可以發(fā)現(xiàn)并加載它們。
下面我們先看看 DispatcherServlet
的繼承關(guān)系:
看到這我們是不是一目了然了, DispatcherServlet
繼承了 HttpServlet
這個(gè)類, HttpServlet
是servlet技術(shù)規(guī)范中專門用于處理http請(qǐng)求的servlet,這就不難解釋為什么spring mvc會(huì)將 DispatcherServlet
作為統(tǒng)一請(qǐng)求入口了。
因?yàn)橐粋€(gè)servlet的生命周期是 init()
-> service()
-> destory()
,那么 DispatcherServlet
怎么初始化呢?看上面的繼承圖,我們進(jìn)到 HttpServletBean
去看看。
果不其然, HttpServletBean
類中有一個(gè) init()
方法, HttpServletBean
是一個(gè)抽象類, init()
方法如下:
可以看出方法采用 final
修飾,因?yàn)?final
修飾的方法是不能被子類繼承的,也就是子類沒(méi)有同樣的 init()
方法了,這個(gè) init
方法就是 DispatcherServlet
的初始化入口了。
接著我們跟進(jìn) FrameworkServlet
的 initServletBean()
方法:
在方法中將會(huì)初始化不同于第一小節(jié)的web容器,請(qǐng)記住,這個(gè)新的spring web 容器是專門為 dispactherServlet
服務(wù)的,而且這個(gè)新容器是在第一小節(jié)根ROOT容器的基礎(chǔ)上創(chuàng)建的,我們?cè)?<servlet>
標(biāo)簽中配置的初始化參數(shù)被加入到新容器中去。
至此, DispatcherSevlet
的初始化完成了,聽(tīng)著有點(diǎn)蒙蔽,但其實(shí)也是這樣,上面的分析僅僅只圍繞一個(gè)方法,它叫 init()
,所有的servlet初始化都將調(diào)用該方法。
總結(jié):
dispactherServlet
的初始化做了兩件事情,第一件事情就是根據(jù)根web容器,也就是我們第一小節(jié)創(chuàng)建的 XmlWebApplicationContext
,然后創(chuàng)建一個(gè)專門為 dispactherServlet
服務(wù)的web容器,第二件事情就是將你在web.xml文件中對(duì) dispactherServlet
進(jìn)行的相關(guān)配置加載到新容器當(dāng)中。
三、每個(gè)request調(diào)用請(qǐng)求經(jīng)歷了哪些過(guò)程
其實(shí)說(shuō)到這才是 dispatcherServlet
控制器的核心所在,因?yàn)閣eb框架無(wú)非就是接受請(qǐng)求,處理請(qǐng)求,然后響應(yīng)請(qǐng)求。當(dāng)然了,如果 dispactherServlet
只是單純地接受處理然后響應(yīng)請(qǐng)求,那未免太弱了,因此spring設(shè)計(jì)者加入了許許多多的新特性,比如說(shuō)攔截器、消息轉(zhuǎn)換器、請(qǐng)求處理映射器以及各種各樣的 Resolver
,因此spring mvc非常強(qiáng)大。
dispatcherServlet
類不做相關(guān)源碼分析,因?yàn)樗褪且粋€(gè)固定的執(zhí)行步驟,什么意思呢?一個(gè)request進(jìn)來(lái),大致就經(jīng)歷這樣的過(guò)程:
接受請(qǐng)求 -----> 是否有各種各樣的處理器 Handler
-------> 是否有消息轉(zhuǎn)換器 HandlerAdapter
--------> 響應(yīng)請(qǐng)求
上面每一步如果存在相應(yīng)的組件,當(dāng)然前提是你在項(xiàng)目中有做相關(guān)的配置,則會(huì)執(zhí)行你配置的組件,最后響應(yīng)請(qǐng)求。因此明白大致的流程之后,如果你想調(diào)試一個(gè)request,那么你完全可以在 dispatcherServlet
類的 doDispatch
方法中打個(gè)斷點(diǎn),跟完代碼之后你就會(huì)發(fā)現(xiàn)其實(shí)大致流程就差不多了。
四、后話
本文的工程是基于傳統(tǒng)的web.xml加載web項(xiàng)目,當(dāng)然在spring mvc中我們也可以完全基于注解的方式進(jìn)行配置,我們可以通過(guò)實(shí)現(xiàn) WebApplicationInitializer
來(lái)創(chuàng)建自己的web啟動(dòng)器,也可以通過(guò)繼承 AbstractAnnotationConfigDispatcherServletInitializer
來(lái)創(chuàng)建相應(yīng)的spring web容器(包括上面說(shuō)到的根容器和servlet web容器),最后通過(guò)繼承 WebMvcConfigurationSupport
再一步進(jìn)行自定義配置(相關(guān)攔截器,bean等)
以上是“Spring MVC啟動(dòng)過(guò)程的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
當(dāng)前標(biāo)題:SpringMVC啟動(dòng)過(guò)程的示例分析-創(chuàng)新互聯(lián)
文章起源:http://www.rwnh.cn/article26/hdicg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供手機(jī)網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)、微信小程序、外貿(mào)網(wǎng)站建設(shè)、關(guān)鍵詞優(yōu)化、網(wǎng)站策劃
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容