背景:
最近在搭建新工程的时候发现有些Spring的配置不是很了解,比如Spring 配置里面明明配置了component-scan,为啥Spring MVC配置文件还需要配置一下,这样岂不是多此一举?由于以前基本是在现有的工程上直接开发或者别的工程的配置文件直接拷贝过来,所以也没太关注这个问题。
于是我去除了application.xml的相关扫描,并在spring-mvc.xml中扫描全部,但是除了一个问题,定时任务的bean找不到了。
正常代码如下:
<!-- 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
<!-- 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>
原理:
原来Spring 是父容器, Spring MVC是子容器, 子容器可以访问父容器的bean,父容器不能访问子容器的bean。
具体参照:
Spring和SpringMVC父子容器关系初窥
Spring为什么不做全局包扫描
Spring与SpringMVC的容器关系分析
测试一: 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,所以没啥问题。
另外:这种方法可以是可以,但是有一些情况需要考虑到,我们有时候会使用 quartz来进行相关定时任务,如果所有的bean放到springMVC里面扫描,这些定时任务的bean会有问题
疑问一: 单例的bean在父子容器中存在一个实例还是两个实例?
答:初始化两次,Spring 容器先初始化bean,MVC容器再初始化bean,所以应该是两个bean。
疑问二:为啥不把所有bean 都在子容器中扫描?
答: 网上很多文章说子容器不支持AOP,其实这是不对的。因为正常会有AOP的相关配置都在Spring容器中配置,如果都迁移到MVC配置文件,则所有bean都在子容器中,相当于只有一个容器了,所以也就实现了AOP。缺点是不利于扩展。
大部分里理由是:Spring MVC管理Controller,Spring 管理Controller之外的Bean,去除Controller应该是Spring的配置
参考:
http://blog.csdn.net/lovesomnus/article/details/51473740