欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring MVC 啟動(dòng)過(guò)程源碼分析詳解

 更新時(shí)間:2018年07月24日 09:15:09   作者:擁抱心中的夢(mèng)想  
這篇文章主要介紹了Spring MVC 啟動(dòng)過(guò)程源碼分析詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

今天小編嘗試從源碼層面上對(duì)Spring mvc的初始化過(guò)程進(jìn)行分析,一起揭開(kāi)Spring mvc的真實(shí)面紗,也許我們都已經(jīng)學(xué)會(huì)使用spring mvc,或者說(shuō)對(duì)spring mvc的原理在理論上已經(jīng)能倒背如流。在開(kāi)始之前,這可能需要你掌握J(rèn)ava EE的一些基本知識(shí),比如說(shuō)我們要先學(xué)會(huì)Java EE 的Servlet技術(shù)規(guī)范,因?yàn)镾pring mvc框架實(shí)現(xiàn),底層是遵循Servlet規(guī)范的。

在開(kāi)始源碼分析之前,我們可能需要一個(gè)簡(jiǎn)單的案例工程,不慌,小編已經(jīng)安排好了:

樣例工程下載地址 : https://github.com/SmallerCoder/spring-mvc-test

那下面就讓我們開(kāi)始吧!

一、前置知識(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-paramlistener 參數(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 源碼中是基于 contextConfigLocationcontextClass 兩個(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ù)配置如 contextConfigLocationcontextClass 創(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í)行,咋一看像一扇門(mén),因此我管它叫“閘門(mén)”。在我們繼續(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ī)范中專門(mén)用于處理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) FrameworkServletinitServletBean() 方法:

在方法中將會(huì)初始化不同于第一小節(jié)的web容器,請(qǐng)記住,這個(gè)新的spring web 容器是專門(mén)為 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è)專門(mén)為 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等)

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 如何在JDK 9中更簡(jiǎn)潔使用 try-with-resources 語(yǔ)句

    如何在JDK 9中更簡(jiǎn)潔使用 try-with-resources 語(yǔ)句

    本文詳細(xì)介紹了自 JDK 7 引入的 try-with-resources 語(yǔ)句的原理和用法,以及介紹了 JDK 9 對(duì) try-with-resources 的改進(jìn),使得用戶可以更加方便、簡(jiǎn)潔的使用 try-with-resources 語(yǔ)句。,需要的朋友可以參考下
    2019-06-06
  • shiro之記住登錄信息

    shiro之記住登錄信息

    Shiro提供了記住我(RememberMe)的功能,當(dāng)關(guān)閉瀏覽器時(shí)下次再次打開(kāi)還能記住你的信息,下面小編給大家分享shiro之記住登錄信息的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2017-09-09
  • Javassist用法詳解

    Javassist用法詳解

    這篇文章主要介紹了Javassist用法的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下
    2021-02-02
  • idea中使用SonarLint進(jìn)行代碼規(guī)范檢測(cè)及使用方法

    idea中使用SonarLint進(jìn)行代碼規(guī)范檢測(cè)及使用方法

    這篇文章主要介紹了idea中使用SonarLint進(jìn)行代碼規(guī)范檢測(cè),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-08-08
  • Java中String類常用類型實(shí)例總結(jié)

    Java中String類常用類型實(shí)例總結(jié)

    在我們開(kāi)發(fā)中經(jīng)常會(huì)用到很多的常用的工具類,這里做一個(gè)總結(jié),下面這篇文章主要給大家介紹了關(guān)于Java中String類常用類型的相關(guān)資料,String類代表字符串,需要的朋友可以參考下
    2021-12-12
  • Java本地高性能緩存的幾種常見(jiàn)實(shí)現(xiàn)方式

    Java本地高性能緩存的幾種常見(jiàn)實(shí)現(xiàn)方式

    在Java中緩存是一種常用的性能優(yōu)化技術(shù),用于在應(yīng)用程序中加速訪問(wèn)和查詢數(shù)據(jù)的速度,下面這篇文章主要給大家介紹了關(guān)于Java本地高性能緩存的幾種常見(jiàn)實(shí)現(xiàn)方式,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-07-07
  • Java Process類的詳解及實(shí)例代碼

    Java Process類的詳解及實(shí)例代碼

    這篇文章主要介紹了Java Process類的詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • Java 實(shí)戰(zhàn)項(xiàng)目錘煉之仿天貓網(wǎng)上商城的實(shí)現(xiàn)流程

    Java 實(shí)戰(zhàn)項(xiàng)目錘煉之仿天貓網(wǎng)上商城的實(shí)現(xiàn)流程

    讀萬(wàn)卷書(shū)不如行萬(wàn)里路,只學(xué)書(shū)上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+jsp+servlet+mysql+ajax實(shí)現(xiàn)一個(gè)仿天貓網(wǎng)上商城項(xiàng)目,大家可以在過(guò)程中查缺補(bǔ)漏,提升水平
    2021-11-11
  • JavaCV簡(jiǎn)介與環(huán)境搭建詳細(xì)步驟

    JavaCV簡(jiǎn)介與環(huán)境搭建詳細(xì)步驟

    JavaCV是一個(gè)開(kāi)源的Java接口,它為幾個(gè)著名的計(jì)算機(jī)視覺(jué)庫(kù)(如OpenCV、FFmpeg)提供了Java封裝,這篇文章主要給大家介紹了關(guān)于JavaCV簡(jiǎn)介與環(huán)境搭建的相關(guān)資料,需要的朋友可以參考下
    2024-04-04
  • Javaweb會(huì)話跟蹤技術(shù)Cookie和Session的具體使用

    Javaweb會(huì)話跟蹤技術(shù)Cookie和Session的具體使用

    本文主要介紹了Javaweb會(huì)話跟蹤技術(shù)Cookie&Session的具體使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07

最新評(píng)論