問題現象 最近在本地調試公司的一個Web項目時,無意中發現日誌中出現了兩次同一個服務的init記錄,項目都是基於Spring來搭建的,按理說服務都是單例的,應該只有一次服務載入日誌才對,本著對工作認真負責(閑來無事)的態度,必然要一探究竟。 問題分析 為什麼同一個 Bean 會被容器初始化兩次? 首 ...
問題現象
最近在本地調試公司的一個Web項目時,無意中發現日誌中出現了兩次同一個服務的init記錄,項目都是基於Spring來搭建的,按理說服務都是單例的,應該只有一次服務載入日誌才對,本著對工作認真負責(閑來無事)的態度,必然要一探究竟。
問題分析
為什麼同一個 Bean 會被容器初始化兩次?
首先,我們先來梳理一下 Web 容器中如何載入 Bean:
在 Web 容器中,ContextLoaderListener
和 DispatchServlet
都會在容器啟動的時候載入
Bean,區別在於 DispatchServlet
一般會載入 MVC 相關的 Bean,ContextLoaderListener
會載入 Spring 相關的 Bean,二者會分別生成一個WebApplicationContext
。
根據 web.xml 的載入順序,listener 會先於 Servlet 載入,當獲取 Bean 時,會優先從
DispatchServlet
生成的 WebApplicationContext
中查找,如果找不到再從ContextLoaderListener
生成的 WebApplicationContext
中查找。
那麼如果這兩個載入了同樣的Bean,到底該用誰的呢?
如果二者的配置文件中定義了相同的 Bean,則實際使用中只會用到 DispatchServlet
中的
Bean,ContextLoaderListener
中的 Bean 無法調用,造成記憶體泄漏。
接下來我們看一下項目中的 web.xml 配置,如下圖所示,ContextLoaderListener
和
DispatchServlet
載入了相同的配置 spring.xml,所以會出現兩次 Bean 的初始化現象。
解決方案
經過上面的分析,我們知道了,之所以同一個Bean會被載入兩次,是由於我們在DispatchServlet
和ContextLoaderListener
都定義了這個Bean。
因此,我們要做的就是讓ContextLoaderListener
和DispatcherServlet
分別載入不同的Bean:
-
新增applicationContext.xml,其中聲明ContextLoaderListener要載入的Bean:
-
修改spring.xml中的包掃描範圍,讓DispatcherServlet只載入mvc相關的Bean:
-
啟動服務,查看初始化信息,Service只被初始化了一次:
本文來自博客園,作者:時鐘在說話,轉載請註明原文鏈接:https://www.cnblogs.com/mindforward/p/16795266.html