背景:
最近在搭建新工程的時候發現有些Spring的配置不是很了解,比如Spring 配置里面明明配置了component-scan,為啥Spring MVC配置文件還需要配置一下,這樣豈不是多此一舉?由於以前基本是在現有的工程上直接開發或者別的工程的配置文件直接拷貝過來,所以也沒太關注這個問題。出於好奇,谷歌了一下發現原來這個里面大有學問呢,詳情請見下文。正常代碼如下:
- <!-- spring 配置文件-->
- <context:component-scan base-package="com.xxx.xxx.account.front">
- <context:exclude-filter type="annotation"
- expression="org.springframework.stereotype.Controller" />
- </context:component-scan>
- <!-- spring mvc -->
- <context:component-scan base-package="com.xxx.xxx.account.front.web" use-default-filters="false">
- <context:include-filter type="annotation"
- expression="org.springframework.stereotype.Controller" />
- </context:component-scan>
測試bean
- @Service
- public class TestService implements InitializingBean {
- @Autowired
- private PersonalAddressAjaxController personalAddressAjaxController;
- @Override
- public void afterPropertiesSet() throws Exception {
- System.out.println("--------------------------------");
- }
- }
原理:
原來Spring 是父容器, Spring MVC是子容器, 子容器可以訪問父容器的bean,父容器不能訪問子容器的bean。
具體參照:
測試一: Spring加載全部bean,MVC加載Controller
- <!-- spring 配置文件-->
- <context:component-scan base-package="com.xxx.xxx.account.front">
- </context:component-scan>
- <!-- spring mvc -->
- <context:component-scan base-package="com.xxx.xxx.account.front.web" use-default-filters="false">
- <context:include-filter type="annotation"
- expression="org.springframework.stereotype.Controller" />
- </context:component-scan>
測試結果:TestService通過,界面顯示正常。
原因:父容器加載了全部bean,所以Service 能訪問到Controller。MVC容器默認查找當前容器,能查到有轉發的Controller規則所以界面正常跳轉。
測試二:Spring加載全部Bean,MVC容器啥也不加載
- <!-- spring 配置文件-->
- <context:component-scan base-package="com.xxx.xxx.account.front">
- </context:component-scan>
- <!-- spring mvc -->
- 無
測試結果:TestService通過,界面顯示404。
原因:父容器加載了全部bean,所以Service 能訪問到Controller。MVC容器默認查找當前容器的Controller,找不到所以界面出現404。
測試三:Spring加載所有除了Controller的bean,MVC只加載Controller
- <!-- spring 配置文件-->
- <context:component-scan base-package="com.xxx.xxx.account.front">
- <context:exclude-filter type="annotation"
- expression="org.springframework.stereotype.Controller" />
- </context:component-scan>
- <!-- spring mvc -->
- <context:component-scan base-package="com.xxx.xxx.account.front.web" use-default-filters="false">
- <context:include-filter type="annotation"
- expression="org.springframework.stereotype.Controller" />
- </context:component-scan>
測試結果:TestService初始化失敗,如果注釋掉該bean,界面正常。
原因:父容器不能訪問子容器的bean。
測試四:Spring不加載bean,MVC加載所有的bean
- <!-- spring 配置文件-->
- 無
- <!-- spring mvc -->
- <context:component-scan base-package="com.xxx.xxx.account.front.web" use-default-filters="true">
- </context:component-scan>
測試結果:TestService通過,界面正常。
原因:因為所有的bean都在子容器中,也能查到當前容器中的Controller,所以沒啥問題。
疑問一: 單例的bean在父子容器中存在一個實例還是兩個實例?
答:初始化兩次,Spring 容器先初始化bean,MVC容器再初始化bean,所以應該是兩個bean。
疑問二:為啥不把所有bean 都在子容器中掃描?
答: 網上很多文章說子容器不支持AOP,其實這是不對的。因為正常會有AOP的相關配置都在Spring容器中配置,如果都遷移到MVC配置文件,則所有bean都在子容器中,相當於只有一個容器了,所以也就實現了AOP。缺點是不利於擴展。