一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Spring MVC 啟動過程源碼分析詳解

Spring MVC 啟動過程源碼分析詳解

2021-05-20 13:49擁抱心中的夢想 Java教程

這篇文章主要介紹了Spring MVC 啟動過程源碼分析詳解,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

今天小編嘗試從源碼層面上對spring mvc的初始化過程進行分析,一起揭開spring mvc的真實面紗,也許我們都已經學會使用spring mvc,或者說對spring mvc的原理在理論上已經能倒背如流。在開始之前,這可能需要你掌握java ee的一些基本知識,比如說我們要先學會java ee 的servlet技術規范,因為spring mvc框架實現,底層是遵循servlet規范的。

在開始源碼分析之前,我們可能需要一個簡單的案例工程,不慌,小編已經安排好了:

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

那下面就讓我們開始吧!

一、前置知識

大家都知道,我們在使用spring mvc時通常會在 web.xml 文件中做如下配置:

web.xml

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?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">
 
 
 <!-- 上下文參數,在監聽器中被使用 -->
 <context-param>
    <param-name>contextconfiglocation</param-name>
    <param-value>
    classpath:applicationcontext.xml
  </param-value>
 </context-param>
 
 
 <!-- 監聽器配置 -->
 <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>

上面的配置總結起來有幾點內容,分別是:dispatcherservlet

當我們將spring mvc應用部署到tomcat時,當你不配置任何的 context-paramlistener 參數,只配置一個 dispatcherservlet 時,那么tomcat在啟動的時候是不會初始化spring web上下文的,換句話說,tomcat是不會初始化spring框架的,因為你并沒有告訴它們spring的配置文件放在什么地方,以及怎么去加載。所以 listener 監聽器幫了我們這個忙,那么為什么配置監聽器之后就可以告訴tomcat怎么去加載呢?因為 listener 是實現了servlet技術規范的監聽器組件,tomcat在啟動時會先加載 web.xml 中是否有servlet監聽器存在,有則啟動它們。 contextloaderlistener 是spring框架對servlet監聽器的一個封裝,本質上還是一個servlet監聽器,所以會被執行,但由于 contextloaderlistener 源碼中是基于 contextconfiglocationcontextclass 兩個配置參數去加載相應配置的,因此就有了我們配置的 context-param 參數了, servlet 標簽里的初始化參數也是同樣的道理,即告訴web服務器在啟動的同時把spring web上下文( webapplicationcontext )也給初始化了。

上面講了下tomcat加載spring mvc應用的大致流程,接下來將從源碼入手分析啟動原理。

二、spring mvc web 上下文啟動源碼分析

假設現在我們把上面 web.xml 文件中的 <load-on-startup>1</load-on-startup> 給去掉,那么默認tomcat啟動時只會初始化spring web上下文,也就是說只會加載到 applicationcontext.xml 這個文件,對于 applicationcontext-mvc.xml 這個配置文件是加載不到的, <load-on-startup>1</load-on-startup> 的意思就是讓 dispatcherservlet 延遲到使用的時候( 也就是處理請求的時候 )再做初始化。

我們已經知道spring web是基于 servlet 標準去封裝的,那么很明顯,servlet怎么初始化, webapplicationcontext web上下文就應該怎么初始化。我們先看看 contextloaderlistener 的源碼是怎樣的。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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 類實現了 servletcontextlistener ,本質上是一個servlet監聽器,tomcat將會優先加載servlet監聽器組件,并調用 contextinitialized 方法,在 contextinitialized 方法中調用 initwebapplicationcontext 方法初始化spring web上下文,看到這煥然大悟,原來spring mvc的入口就在這里,哈哈~~~趕緊跟進去 initwebapplicationcontext 方法看看吧!

initwebapplicationcontext() 方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 創建web上下文,默認是xmlwebapplicationcontext
if (this.context == null) {
 this.context = createwebapplicationcontext(servletcontext);
}
 
if (this.context instanceof configurablewebapplicationcontext) {
 configurablewebapplicationcontext cwac = (configurablewebapplicationcontext) this.context;
 // 如果該容器還沒有刷新過
 if (!cwac.isactive()) {
    if (cwac.getparent() == null) {
        applicationcontext parent = loadparentcontext(servletcontext);
        cwac.setparent(parent);
    }
    // 配置并刷新容器
    configureandrefreshwebapplicationcontext(cwac, servletcontext);
 }
}

上面的方法只做了兩件事:

1、如果spring web容器還沒有創建,那么就創建一個全新的spring web容器,并且該容器為root根容器,下面第三節講到的servlet spring web容器是在此根容器上創建起來的

2、配置并刷新容器

上面代碼注釋說到默認創建的上下文容器是 xmlwebapplicationcontext ,為什么不是其他web上下文呢?為啥不是下面上下文的任何一種呢?

Spring MVC 啟動過程源碼分析詳解

我們可以跟進去 createwebapplicationcontext 后就可以發現默認是從一個叫 contextloader.properties 文件加載配置的,該文件的內容為:

 

復制代碼 代碼如下:
org.springframework.web.context.webapplicationcontext=org.springframework.web.context.support.xmlwebapplicationcontext

 

具體實現為:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protected class<?> determinecontextclass(servletcontext servletcontext) {
 // 自定義上下文,否則就默認創建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);
  }
 }
}

上面可以看出其實我們也可以自定義spring web的上下文的,那么怎么去指定我們自定義的上下文呢?答案是通過在 web.xml 中指定 contextclass 參數,因此第一小結結尾時說 contextclass 參數和 contextconfiglocation 很重要~~至于 contextconfiglocation 參數,我們跟進 configureandrefreshwebapplicationcontext 即可看到,如下圖:

Spring MVC 啟動過程源碼分析詳解

總結:

spring mvc啟動流程大致就是從一個叫 contextloaderlistener 開始的,它是一個servlet監聽器,能夠被web容器發現并加載,初始化監聽器 contextloaderlistener 之后,接著就是根據配置如 contextconfiglocationcontextclass 創建web容器了,如果你不指定 contextclass 參數值,則默認創建的spring web容器類型為 xmlwebapplicationcontext ,最后一步就是根據你配置的 contextconfiglocation 文件路徑去配置并刷新容器了。

三、 dispatcherservlet 控制器的初始化

好了,上面我們簡單地分析了spring mvc容器初始化的源碼,我們永遠不會忘記,我們默認創建的容器類型為 xmlwebapplicationcontext ,當然我們也不會忘記,在 web.xml 中,我們還有一個重要的配置,那就是 dispatcherservlet 。下面我們就來分析下 dispatcherservlet 的初始化過程。

dispatcherservlet ,就是一個servlet,一個用來處理request請求的servlet,它是spring mvc的核心,所有的請求都經過它,并由它指定后續操作該怎么執行,咋一看像一扇門,因此我管它叫“閘門”。在我們繼續之前,我們應該共同遵守一個常識,那就是-------無論是監聽器還是servlet,都是servlet規范組件,web服務器都可以發現并加載它們。

下面我們先看看 dispatcherservlet 的繼承關系:

Spring MVC 啟動過程源碼分析詳解

看到這我們是不是一目了然了, dispatcherservlet 繼承了 httpservlet 這個類, httpservlet 是servlet技術規范中專門用于處理http請求的servlet,這就不難解釋為什么spring mvc會將 dispatcherservlet 作為統一請求入口了。

因為一個servlet的生命周期是 init() -> service() -> destory() ,那么 dispatcherservlet 怎么初始化呢?看上面的繼承圖,我們進到 httpservletbean 去看看。

果不其然, httpservletbean 類中有一個 init() 方法, httpservletbean 是一個抽象類, init() 方法如下:

Spring MVC 啟動過程源碼分析詳解

可以看出方法采用 final 修飾,因為 final 修飾的方法是不能被子類繼承的,也就是子類沒有同樣的 init() 方法了,這個 init 方法就是 dispatcherservlet 的初始化入口了。

接著我們跟進 frameworkservletinitservletbean() 方法:

Spring MVC 啟動過程源碼分析詳解

在方法中將會初始化不同于第一小節的web容器,請記住,這個新的spring web 容器是專門為 dispactherservlet 服務的,而且這個新容器是在第一小節根root容器的基礎上創建的,我們在 <servlet> 標簽中配置的初始化參數被加入到新容器中去。

至此, dispatchersevlet 的初始化完成了,聽著有點蒙蔽,但其實也是這樣,上面的分析僅僅只圍繞一個方法,它叫 init() ,所有的servlet初始化都將調用該方法。

總結:

dispactherservlet 的初始化做了兩件事情,第一件事情就是根據根web容器,也就是我們第一小節創建的 xmlwebapplicationcontext ,然后創建一個專門為 dispactherservlet 服務的web容器,第二件事情就是將你在web.xml文件中對 dispactherservlet 進行的相關配置加載到新容器當中。

三、每個request調用請求經歷了哪些過程

其實說到這才是 dispatcherservlet 控制器的核心所在,因為web框架無非就是接受請求,處理請求,然后響應請求。當然了,如果 dispactherservlet 只是單純地接受處理然后響應請求,那未免太弱了,因此spring設計者加入了許許多多的新特性,比如說攔截器、消息轉換器、請求處理映射器以及各種各樣的 resolver ,因此spring mvc非常強大。

dispatcherservlet 類不做相關源碼分析,因為它就是一個固定的執行步驟,什么意思呢?一個request進來,大致就經歷這樣的過程:

接受請求 -----> 是否有各種各樣的處理器 handler -------> 是否有消息轉換器 handleradapter --------> 響應請求

上面每一步如果存在相應的組件,當然前提是你在項目中有做相關的配置,則會執行你配置的組件,最后響應請求。因此明白大致的流程之后,如果你想調試一個request,那么你完全可以在 dispatcherservlet 類的 dodispatch 方法中打個斷點,跟完代碼之后你就會發現其實大致流程就差不多了。

四、后話

本文的工程是基于傳統的web.xml加載web項目,當然在spring mvc中我們也可以完全基于注解的方式進行配置,我們可以通過實現 webapplicationinitializer 來創建自己的web啟動器,也可以通過繼承 abstractannotationconfigdispatcherservletinitializer 來創建相應的spring web容器(包括上面說到的根容器和servlet web容器),最后通過繼承 webmvcconfigurationsupport 再一步進行自定義配置(相關攔截器,bean等)

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://juejin.im/post/5b207dc86fb9a01e49294f42

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 99久久精品6在线播放 | bl文全肉高h湿被灌尿 | h网站国产 | 深夜激情网 | 亚洲色图欧美色 | 91久久综合 | 人阁色第四影院在线观看 | 性欧美xxxxx老太婆 | 日韩一区二区三区不卡视频 | 亚洲成人77777| 亚州日韩精品AV片无码中文 | 18young第一次| 青草国产在线观看 | 久久久久久久伊人电影 | 免费在线观看中文字幕 | 天天操天天射天天爽 | 男人天堂久久 | 国产真实一区二区三区 | 欧美午夜网站 | 日本xxwwwxxxx | 亚洲六月丁香六月婷婷色伊人 | 四虎影视在线观看永久地址 | 亚洲午夜久久久久久91 | 99久久综合久中文字幕 | 精品久久久久久久久久香蕉 | caopo视频进入离开 | 操美女 | 亚洲卡一卡2卡三卡4麻豆 | 操bb视频| 亚洲qvod图片区电影 | 日本高清视频网址 | 国产成人盗拍精品免费视频 | 热99re久久精品精品免费 | 四虎精品永久在线网址 | 四虎色影院 | 私人影院免费观看 | 大肥臀风间由美 中文字幕 大东北chinesexxxx露脸 | 日本狠狠操 | 亚洲视频在线观看不卡 | 男女男精品视频免费观看 | 欧美一区二区三区不卡视频 |