一般比較簡單的項目,我們通常會用普遍簡單的方式去配置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> 默認
</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"/>