boot spring 沒有父子容器_Spring 系列(二):Spring MVC的父子容器

e79797c7639a682c8454b49b3eaa35d1.png

1.背景

在使用Spring MVC時候大部分同學都會定義兩個配置文件,一個是Spring的配置文件spring.xml,另一個是Spring MVC的配置文件spring-mvc.xml。

在這里給大家拋個問題,如果在spring.xml和spring-mvc.xml文件中同時定義一個相同id的單例bean會怎樣呢?大家可以先思考一下再繼續往下看。

我做了個實驗,結論是:容器中會同時存在兩個相同id 的bean,而且使用起來互不干擾。

這是為什么呢?學過Spring的同學肯定會質疑,眾所周知id是bean的唯一標示,怎么可能同時存在兩個相同id的bean呢?是不是我在胡扯呢?

原諒我在這和大家賣了個關子,其實大家說的都沒錯,因為這里涉及到Spring MVC父子容器的知識點。

這個知識點是:在使用Spring MVC過程中會存在Spring MVC 、Spring兩個IOC容器,且Spring MVC是Spring的子容器。

那這個父子容器到底是什么呢?

為了保證我所說的權威性,而不是知識的二道販子,我將從Spring 官方文檔和源碼兩方面展開介紹。

2.Spring MVC父子容器

2.1 web.xml配置

還是先找程序入口,查看web.xml配置文件,找到Spring MVC相關配置。

<servlet><servlet-name>spring-mvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-mvc.xml</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet>

配置很簡單,只是配置了一個類型為DispatcherServlet類型的Servlet,并設置了初始化參數。那DispatcherServlet是什么呢?

2.2 DispatcherServlet類介紹

查看API文檔

74e3f3ff36d48fdc40e2f9b2e29775e3.png

從繼承圖看出最終繼承自HttpServlet,其實就是一個普通的Servlet。那為什么這個Servlet就能完成Spring MVC一系列復雜的功能呢?繼續往下看。

2.3 DispatcherServlet工作流程

769ff1f7df0de658847cabb78e304013.png

DispatcherServlet工作流程如下:

(1) 所有請求先發到DispacherServlet 。

(2) DispacherServlet根據請求地址去查詢相應的Controller,然后返回給DispacherServlet。

(3) DispacherServlet得到Controller后,讓Controler處理相應的業務邏輯。

(4) Controler處理處理完后將結果返回給DispacherServlet。

(5) DispacherServlet把得到的結果用視圖解析器解析后獲得對應的頁面。

(6) DispacherServlet跳轉到解析后的頁面。

在整個過程中DispatcherServlet承當了一個中心控制器的角色來處理各種請求。

2.4 DispatcherServlet上下文繼承關系

2384a12957435ba27d4af611e1b5d008.png

上圖來自Spring官網:

https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html

從圖中可以看到DispatcherServlet里面有一個 Servlet WebApplicationContext,繼承自 Root WebApplicationContext。

從上篇文章中我們知道WebApplicationContext其實就是一個IOC容器,root WebApplicationContext是Spring容器。

這說明DispatcherServlet中里創建了一個IOC容器并且這個容器繼承了Spring 容器,也就是Spring的子容器。

而且官方文檔中還有如下一段文字描述:

For many applications, having a single WebApplicationContext is simple and suffices. It is also possible to have a context hierarchy where one root WebApplicationContext is shared across multiple DispatcherServlet (or other Servlet) instances, each with its own child WebApplicationContext configuration. See Additional Capabilities of the ApplicationContext for more on the context hierarchy feature.The root WebApplicationContext typically contains infrastructure beans, such as data repositories and business services that need to be shared across multiple Servlet instances.
Those beans are effectively inherited and can be overridden (that is, re-declared) in the Servlet-specific child WebApplicationContext, which typically contains beans local to the given Servlet.

結合圖和上述文字我們可以得出以下信息:

  1. 應用中可以包含多個IOC容器。
  2. DispatcherServlet的創建的子容器主要包含Controller、view resolvers等和web相關的一些bean。
  3. 父容器root WebApplicationContex主要包含包含一些基礎的bean,比如一些需要在多個servlet共享的dao、service等bean。
  4. 如果在子容器中找不到bean的時候可以去父容器查找bean。

看到這里也許大家心中也許就明白文章開頭中我說的Spring MVC中的父子容器了,對那個問題也有了自己的判斷和答案。

當然文章還沒有結束,畢竟這還僅限于對官方文檔的理解,為了進一步驗證,我們拿出終極武器:

閱讀源碼!

2.5 DispatcherServlet源碼分析

本小節我們分為Spring MVC容器的創建和bean的獲取兩部分進行分析。

2.5.1 Spring MVC容器的創建

前面分析到DispatcherServlet本質上還是一個Servlet ,既然是Servlet ,了解Servlet生命周期的同學都知道Web 容器裝載Servlet第一步是執行init()函數,因此以DispatcherServlet 的init函數為突破口進行分析。

@Override
public final void init() throws ServletException {// 1.讀取init parameters 等參數,其中就包括設置contextConfigLocation PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);//2.初始化servlet中使用的beaninitServletBean();
}

在第1步讀取init parameter的函數最終會調用setContextConfigLocation()設置配置文件路徑。此處重點介紹initServletBean(),繼續跟蹤。

Override
protected final void initServletBean() throws ServletException {//初始化webApplicationContextthis.webApplicationContext = initWebApplicationContext();
}
protected WebApplicationContext initWebApplicationContext() {//1.獲得rootWebApplicationContextWebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;//2.如果還沒有webApplicatioinContext,創建webApplicationContextif (wac == null) {//創建webApplicationContextwac = createWebApplicationContext(rootContext);}return wac;
}

可以看到上面初始化webApplicationContext分為2步。

(1)獲取父容器rootWebApplicationContext。

(2)創建子容器。

我們先看看rootWebApplicationContext是如何獲取的。

public static WebApplicationContext getWebApplicationContext(ServletContext sc) {return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {Object attr = sc.getAttribute(attrName);return (WebApplicationContext) attr;
}

從上面代碼中我沒看到是從ServletContext獲取了名為“WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE”的webApplicationContext。

認真看過上篇文章的同學應該記得這個屬性是在Spring初始化 容器initWebApplicationContext()函數中的第3步設置進去的,取得的值即Spring IOC容器。

繼續看如何創建webApplicationContext。

protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {return createWebApplicationContext((ApplicationContext) parent);
}
createWebApplicationContext(ApplicationContext parent) {//1.獲取WebApplicationContext實現類,此處其實就是XmlWebApplicationContextClass<?> contextClass = getContextClass();//生成XmlWebApplicationContext實例ConfigurableWebApplicationContext wac =(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);//2.設置rootWebApplicationContext為父容器 wac.setParent(parent);//3.設置配置文件wac.setConfigLocation(getContextConfigLocation());//4.配置webApplicationContext.configureAndRefreshWebApplicationContext(wac);return wac;
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {//開始處理beanwac.refresh();
}

看到這里同學們有沒有是曾相識的感覺。是的,這段邏輯和上篇文章創建Spring IOC的邏輯類似。

唯一不同的是在第2步會把Spring容器設置為自己的父容器。至于新建容器中bean的注冊、解析、實例化等流程和Spring IOC容器一樣都是交給XmlWebApplicationContext類處理,還沒有掌握的同學可以看上篇文章。

2.5.2 Spring MVC Bean的獲取

Spring MVC bean的獲取其實我們在上篇文章已經介紹過,這次再單拎出來介紹一下,加深記憶。

protected <T> T doGetBean(// 獲取父BeanFactoryBeanFactory parentBeanFactory = getParentBeanFactory();//如果父容器不為空,且本容器沒有注冊此bean就去父容器中獲取beanif (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// 如果父容器有該bean,則調用父beanFactory的方法獲得該beanreturn (T) parentBeanFactory.getBean(nameToLookup,args);}//如果子容器注冊了bean,執行一系列實例化bean操作后返回bean.//此處省略實例化過程.....return (T) bean;
}

上面代碼就可以對應官方文檔中“如果子容器中找不到bean,就去父容器找”的解釋了。

3.小結

看完上面的介紹,相信大家對Spring MVC父子容器的概念都有所了解,現在我們分析文章開頭的問題。

如果spring.xml和spring-mvc.xml定義了相同id的bean會怎樣?假設id=test。

1.首先Spring 初始化,Spring IOC 容器中生成一個id為test bean實例。

2.Spring MVC開始初始化,生成一個id為test bean實例。

此時,兩個容器分別有一個相同id的bean。那用起來會不會混淆?

答案是不會。

當你在Spring MVC業務邏輯中使用該bean時,Spring MVC會直接返回自己容器的bean。

當你在Spring業務邏輯中使用該bean時,因為子容器的bean對父親是不可見的,因此會直接返回Spring容器中的bean。

雖然上面的寫法不會造成問題。但是在實際使用過程中,建議大家都把bean定義都寫在spring.xml文件中。

因為使用單例bean的初衷是在IOC容器中只存在一個實例,如果兩個配置文件都定義,會產生兩個相同的實例,造成資源的浪費,也容易在某些場景下引發缺陷。

4.尾聲

現在大家基本都不使用在xml文件中定義bean的形式,而是用注解來定義bean,然后在xml文件中定義掃描包。如下:

<context:component-scan base-package="xx.xx.xx"/>

那如果在spring.xml和spring-mvc.xml配置了重復的包會怎樣呢?

如果本文看明白的同學此時已經知道了答案。

答案是會在兩個父子IOC容器中生成大量的相同bean,這就會造成內存資源的浪費。

也許有同學想到,那只在spring.xml中設置掃描包不就能避免這種問題發生了嗎,答案是這樣嗎?

大家可以試試,這樣會有什么問題。如果不行,那是為什么呢?

欲知分曉,敬請期待下篇分解!

如果想獲得更多,歡迎關注公眾號:七分熟pizza

公眾號里我會分享更多技術以及職場方面的經驗,大家有什么問題也可以直接在公眾號向我提問交流。

a39d55d111fed42ff4bca168e6e39559.png

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/539798.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/539798.shtml
英文地址,請注明出處:http://en.pswp.cn/news/539798.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux創建目錄【命令】

創建一個hello目錄 mkdir /hello -------------------------------------- mkdir 相當于 make directory 相對路徑&#xff1a;不從/開始&#xff0c;而是從當前目錄開始&#xff0c;例如&#xff1a;data/ ,mnt/zmg絕對路徑&#xff1a;從/開始的目錄&#xff0c;就叫絕對…

php string常用函數

<?php$a[]a;$a[]b;$a[]C;echo "</br>";/* implode — 將一個一維數組的值轉化為字符串說明string implode ( string $glue , array $pieces )string implode ( array $pieces )用 glue 將一維數組的值連接為一個字符串。 參數glue 默認為空的字符串。 pie…

計算機配置的內存的容量為1GB,如果某計算機的內存尋址空間是1GB,那么這臺計算機地址總線的線數為()根。A.32B.20C.16D.30 - 試題答案網問答...

相關題目與解析計算機內存的容量大小受到(11)位數的限制。若該總線為20位&#xff0c;可以尋址的內存空間為(12)字節。A&#xff0e;地址某計算機數據總線為8位&#xff0c;地址總線為10位&#xff0c;則CPU可以直接尋址的內存空間范圍為______字節。A&#xff0e;28B&#xff…

手術后多久可以做膽摘除_近視手術后多久可以化眼妝?

今天小編就和大家聊聊&#xff0c;做完近視手術后&#xff0c;多久可以畫眼妝&#xff1f;很多女生做完手術后&#xff0c;非常關心的一件事情就是多久可以化妝&#xff0c;化妝對手術效果有沒有影響&#xff1f;今天&#xff0c;小編就此問題特別咨詢了屈光手術專家。專家建議…

Linux創建文件【命令】

在/opt/hello 目錄下創建 world.txt 使用命令&#xff1a;touch 文件名 touch world.txt ---------------------------------------- touch “摸”。touch[文件名]&#xff0c;就是摸一下文件&#xff0c;如果文件不存在&#xff0c;就建立新文件。如果存在&#xff0c;就改…

【CentOS 7LAMP架構4】,PHP5和PHP7的安裝和配置#171219

2019獨角獸企業重金招聘Python工程師標準>>> hellopasswd 安裝PHP5 PHP官網www.php.net當前主流版本為5.6/7.1cd /usr/local/srcwget http://cn2.php.net/distributions/php-5.6.30.tar.bz2bzip2 -d php-5.6.30.tar.bz2tar xvf php-5.6.30.tarcd php-5.6.30./config…

Linux刪除文件和文件夾【命令】

1、刪除文件夾&#xff1a;rm -r /opt/hello 將會刪除/opt/hello目錄以及其下所有文件、文件夾 2、刪除文件&#xff1a;rm -f /opt/hello/world.txt 將會強制刪除/opt/hello/world.txt這個文件 使用這個rm 的時候一定要格外小心&#xff0c;linux沒有回收站,以至于刪除之后…

Linux修改文件內容【命令】

在/opt/hello/world.txt文件中增加一行 hello linux world ! 方法一&#xff1a; 命令是&#xff1a;vi&#xff0c;vim vi 編輯器&#xff0c;相當于記事本&#xff0c;有編輯功能&#xff0c;但較弱 vim 復雜的編輯器&#xff0c;相當于windows的 editplus, notepad 等 …

計算機機房管理具體工作和職責,機房管理

心得體會是指一種讀書、實踐后所寫的感受性文字。體會是指將學習的東西運用到實踐中去&#xff0c;通過實踐反思學習內容并記錄下來的文字。下面是出國留學網小編帶來的機房管理工作心得體會范文&#xff0c;歡迎閱讀參考。機房管理工作心得體會(一)學校計算機房是信息技術教學…

hadoop中的9000端口代表什么_hadoop服務快速部署

這篇文章記錄下針對不同的hadoop版本進行服務部署的過程&#xff0c;希望可以幫到你們安裝docker hadoop2.7.0一鍵部署docker hadoop3.0.0集群(一個master 三個slave)安裝docker hadoop 3.2.0 a、docker啟動 b、docker compose方式啟動安裝hadoop 2.7.0版本安裝命令docker run …

來自我的破船大大的博客,記錄他的iOS成長之路,與君同勉!

注1&#xff1a;這篇文章是我的iOS成長之路系列文章中的第三篇文章&#xff0c;第一篇文章&#xff1a;iOS成長之路-1-入門&#xff0c;第二篇文章&#xff1a;iOS成長之路-2-我的第一個iOS Demo。通過兩周時間來學習iOS&#xff0c;我慢慢的叩開了iOS開發的大門&#xff0c;接…

Linux下Oracle移植數據

案例&#xff1a; 老LTESQM數據庫下的toolbox用戶數據移植到 新LTESQM數據庫下的 toolbox 用戶。 新LTESQM數據庫下還沒有toolbox這個用戶 開始&#xff1a; 1、查看老LTESQM下toolbox的權限 用oracle數據庫dba角色登錄&#xff0c;查看用戶toolbox的sql。 使用sqldevelo…

迷你世界電鋸機器人_迷你世界:生產果凍的機器人,1分鐘產出500個,10種口味隨意挑選...

迷你世界是一款具有創意性和想象力的3D沙盒游戲&#xff0c;在游戲中玩家們總是能造出各種好玩的建筑物&#xff0c;畢竟背包里擁有成百上千的道具可供玩家們隨意使用&#xff0c;而且一些道具相互疊加使用&#xff0c;還能產生更有趣的效果。近日就有一位大神使用多種道具&…

電腦計算機顯示向程序發送命令時出現問題,word提示向程序發送命令時出現問題怎么解決 wo...

word提示向程序發送命令時出現問題怎么解決 word提示向程序發送命令時出現問題解決方法 我們的生活中越來越離不開電腦&#xff0c;但在使用電腦的過程中總會遇到各種各樣的問題。知識屋致力于給電腦小白朋友們介紹一些簡單常用問題的解決方法&#xff0c;這次給大家介紹的是wo…

底層實現紅黑樹_圖解:紅黑樹

注&#xff1a;本文比較硬核但是很值得大家花心思看完&#xff0c;看完你一定會有所收獲的紅黑樹是面試中一個很經典也很有難度的知識點&#xff0c;網傳字節跳動面試官最喜歡問這個問題。很多人會覺得這個知識點太難&#xff0c;不想花太多功夫去了解&#xff0c;也有人會認為…

如何請教問題且以后都會回答你的方法

一、如何請教問題 在網上與人溝通時&#xff0c;學會客氣&#xff0c;并且開門見山的把問題說清楚非常必要&#xff0c;還要帶上你努力的過程&#xff0c; 并且盡量不要問開放式的問題&#xff0c;例如&#xff1a;你問一句&#xff0c;回答者可能要10句才能解答的問題不要問…

計算機編程方程求解的步驟,計算機解決問題的過程PPT學習課件

《計算機解決問題的過程PPT學習課件》由會員分享&#xff0c;可在線閱讀&#xff0c;更多相關《計算機解決問題的過程PPT學習課件(17頁珍藏版)》請在人人文庫網上搜索。1、計算機解決問題的過程包括&#xff1a;編程&#xff0c;1&#xff0c;同&#xff0c;2&#xff0c;1&…

處于停機等非正常狀態_一文聊透 Dubbo 優雅停機

1 前言一年之前&#xff0c;我曾經寫過一篇《研究優雅停機時的一點思考》&#xff0c;主要介紹了 kill -9&#xff0c;kill -15 兩個 Linux 指令的含義&#xff0c;并且針對性的聊到了 Spring Boot 應用如何正確的優雅停機&#xff0c;算是本文的前置文章&#xff0c;如果你對上…

Centos 配置eth0 提示Device does not seem to be present

一.故障現象&#xff1a;[rootc1node01 ~]# service network restartShutting down loopback insterface: [ OK ]Bringing up loopback insterface: [ …

計算機boot進入u盤啟動,暗影精靈5怎么設置u盤啟動 暗影精靈5設置u盤啟動方法...

最近有位電腦用戶想要使用u盤啟動盤重裝系統&#xff0c;但是卻不知道應該怎么使用bios設置u盤啟動&#xff0c;為此非常苦惱&#xff0c;那么惠普暗影精靈5 omen 15-dc1068tx筆記本怎么使用bios設置u盤啟動呢?下面為大家介紹惠普暗影精靈5 omen 15-dc1068tx筆記本使用bios設置…