1. 程式人生 > >為啥Spring和Spring MVC包掃描要分開?

為啥Spring和Spring MVC包掃描要分開?

ESS 原來 html htm ID vat 收藏 不支持 不能

背景:

最近在搭建新工程的時候發現有些Spring的配置不是很了解,比如Spring 配置裏面明明配置了component-scan,為啥Spring MVC配置文件還需要配置一下,這樣豈不是多此一舉?由於以前基本是在現有的工程上直接開發或者別的工程的配置文件直接拷貝過來,所以也沒太關註這個問題。出於好奇,谷歌了一下發現原來這個裏面大有學問呢,詳情請見下文。正常代碼如下:

Xml代碼 技術分享圖片
  1. <!-- spring 配置文件-->
  2. <context:component-scan base-package="com.xxx.xxx.account.front">
  3. <context:exclude-filter type="annotation"
  4. expression="org.springframework.stereotype.Controller" />
  5. </context:component-scan>
  6. <!-- spring mvc -->
  7. <context:component-scan base-package="com.xxx.xxx.account.front.web" use-default-filters="false">
  8. <context:include-filter type="annotation"
  9. expression="org.springframework.stereotype.Controller" />
  10. </context:component-scan>

測試bean

Java代碼 技術分享圖片
  1. @Service
  2. public class TestService implements InitializingBean {
  3. @Autowired
  4. private PersonalAddressAjaxController personalAddressAjaxController;
  5. @Override
  6. public void afterPropertiesSet() throws Exception {
  7. System.out.println("--------------------------------");
  8. }
  9. }

原理:

原來Spring 是父容器, Spring MVC是子容器, 子容器可以訪問父容器的bean,父容器不能訪問子容器的bean。

具體參照:

Spring和SpringMVC父子容器關系初窺

Spring為什麽不做全局包掃描

Spring與SpringMVC的容器關系分析

測試一: Spring加載全部bean,MVC加載Controller

Xml代碼 技術分享圖片
  1. <!-- spring 配置文件-->
  2. <context:component-scan base-package="com.xxx.xxx.account.front">
  3. </context:component-scan>
  4. <!-- spring mvc -->
  5. <context:component-scan base-package="com.xxx.xxx.account.front.web" use-default-filters="false">
  6. <context:include-filter type="annotation"
  7. expression="org.springframework.stereotype.Controller" />
  8. </context:component-scan>

測試結果:TestService通過,界面顯示正常。

原因:父容器加載了全部bean,所以Service 能訪問到Controller。MVC容器默認查找當前容器,能查到有轉發的Controller規則所以界面正常跳轉。

測試二:Spring加載全部Bean,MVC容器啥也不加載

Xml代碼 技術分享圖片
  1. <!-- spring 配置文件-->
  2. <context:component-scan base-package="com.xxx.xxx.account.front">
  3. </context:component-scan>
  4. <!-- spring mvc -->

測試結果:TestService通過,界面顯示404。

原因:父容器加載了全部bean,所以Service 能訪問到Controller。MVC容器默認查找當前容器的Controller,找不到所以界面出現404。

測試三:Spring加載所有除了Controller的bean,MVC只加載Controller

Xml代碼 技術分享圖片
  1. <!-- spring 配置文件-->
  2. <context:component-scan base-package="com.xxx.xxx.account.front">
  3. <context:exclude-filter type="annotation"
  4. expression="org.springframework.stereotype.Controller" />
  5. </context:component-scan>
  6. <!-- spring mvc -->
  7. <context:component-scan base-package="com.xxx.xxx.account.front.web" use-default-filters="false">
  8. <context:include-filter type="annotation"
  9. expression="org.springframework.stereotype.Controller" />
  10. </context:component-scan>

測試結果:TestService初始化失敗,如果註釋掉該bean,界面正常。

原因:父容器不能訪問子容器的bean。

測試四:Spring不加載bean,MVC加載所有的bean

Xml代碼 技術分享圖片
  1. <!-- spring 配置文件-->
  2. <!-- spring mvc -->
  3. <context:component-scan base-package="com.xxx.xxx.account.front.web" use-default-filters="true">
  4. </context:component-scan>

測試結果:TestService通過,界面正常。

原因:因為所有的bean都在子容器中,也能查到當前容器中的Controller,所以沒啥問題。

疑問一: 單例的bean在父子容器中存在一個實例還是兩個實例?

答:初始化兩次,Spring 容器先初始化bean,MVC容器再初始化bean,所以應該是兩個bean。

疑問二:為啥不把所有bean 都在子容器中掃描?

答: 網上很多文章說子容器不支持AOP,其實這是不對的。因為正常會有AOP的相關配置都在Spring容器中配置,如果都遷移到MVC配置文件,則所有bean都在子容器中,相當於只有一個容器了,所以也就實現了AOP。缺點是不利於擴展。

為啥Spring和Spring MVC包掃描要分開?