javaweb sping項目中ContextConfigLocation中指定文件位置的設置規則

一般比較簡單的項目,我們通常會用普遍簡單的方式去配置web.xml比如:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</context-param>

但是實際中呢,一般公司是不會把dao和service直接以bean的方式放入application.xml中的,而是分別爲dao和service弄一個新的配置文件,一般是以dao.xml和service.xml形式的名字弄一個配置文件,之後,再在web.xml中配置param-value

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:/main/config/*.xml</param-value>
</context-param>

採用通配符進行匹配。在公司中,肯定不可能像練習那樣,所有東西都放到一起,可能自己認爲,放到一起更方便看和管理,但是一個真正的公司項目是很大的,由多人分工,因此模塊化,更易於管理和分配(剛好我們公司就採用如此配置,導致我有些疑惑,進而查證一番,拼湊了這個帖子以供大家參考)。
採用上述配置時,如果設置不當,則會引起雙重加載controller層的bean類,如下圖所示,是通用的設置SpringMvc上下文的方式,在這裏插入代碼片

<servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 可以自定義servlet.xml配置文件的位置和名稱,默認爲WEB-INF目錄下,名稱爲[<servlet-name>]-servlet.xml,如spring-servlet.xml
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-servlet.xml</param-value>&nbsp; 默認
    </init-param>
    -->
    <load-on-startup>1</load-on-startup>
</servlet>

這樣的話,當web.xml中的ContextConfigLocation採用上述的通配符方式設置文件加載位置的話,就會產生重複加載,雖然不至於出問題,但是其實並不是很妥當,至於原因,如下所述:

項目引入Spring MVC後,實際上已經存在兩個Spring容器:

Spring 父容器:

1、管理業務層bean

2、Web容器啓動時會觸發ServletContextListener事件,從而啓動了Spring父容器,從Spring容器啓動順序看,它是首先被啓動的

3、它會掃描指定包名下面所有標註的類,並裝配到容器,@Controller配置的bean也不例外。

4、Web層可以通過WebApplicationContextUtils.getWebApplicationContext(servletContext)取得Spring父容器

5、父容器不能訪問子容器的bean

Spring MVC子容器:

1、管理Web層bean

2、DispatcherServlet是標準的Servlet,它設置了加載順序爲2,則未訪問的情況下就會被Web容器實例化並執行init()初始化方法,方法中啓動了Spring MVC子容器,因此從順序上說,它是在父容器之後啓動的。

3、它會掃描指定包名下面所有標註的類,並裝配到容器,@Service等等配置的bean也不例外。

4、它的父親是上面的父容器,這一點要注意。

5、可以通過WebApplicationContextUtils.getWebApplicationContext(servletContext,”org.springframework.web.servlet.FrameworkServlet.CONTEXT.” + DispatcherServlet的servlet名)取得

6、子容器可以訪問父容器的bean

因此,兩個容器重複掃描時,會在兩個容器裏出現相同類型但不同實例的Bean(形象地說就內存地址不一樣),即Controller會在兩個容器中實例化,但是父容器裏的基本用不着(千萬不要做從下而上逆向調用的設計),原因是從web容器進入來的請求首先會被DispatcherServlet捕捉到,而DispatcherServlet會交給Spring MVC容器處理,也就是說從子容器取出bean來處理,當子容器沒有對應的bean時,則會從父容器找。

最後說下這種情況下的最佳實踐:

假如某項目包名規劃如下:

org.howsun.domain;

org.howsun.dao;

org.howsun.service;

org.howsun.web;

Spring父容器可以掃描到“org.howsun”,但是要排除掉@Controller bean:

XML/HTML code

<context:component-scan base-package="org.howsun">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

Spring MVC 子容器應該只掃描到“org.howsun.web”

XML/HTML code

<context:component-scan base-package="org.howsun.web"/>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章