何为父子容器?
Spring正题框架的核心概念中, 容器是核心思想。用来管理bean 的整个生命周期,在一个项目中,容器不仅仅只有一个。容器之间也存在上下层关系,当一个项目中引入Spring和SpringMVC这两个框架,其实就是2个容器,Spring是父容器,SpringMVC是其子容器,
子容器可以访问父容器对象,而父容器不可以访问子容器对象。(对父子属性不成立)
该如何理解子容器可以访问父容器对象,而父容器不可以访问子容器对象? 举个栗子
Web层的congtroller定义的类中,可以引用service层的对象(的接口,IC即对象),相反则不成立。
好了,接下来进入讨论的关键:
当我们使用注解开发的时候,对于Bean 的注册,我们使用如下的配置:
<context:component-scan base-package=“com.it.wu" />
Spring参考手册中讲到,该配置的功能是扫描默认包下的所有的@Component注解,并且自动注册到容器(生成bean,简化了xml文件中配置的繁琐工作)中,同时也扫描@Controller,@Service,@Respository这三个注解注册他们所对应的bean到对应的容器中,因为他们是继承自@Component。
以上的这个注解配置,使用于父类和子类容器。
当然,还有一个SpringMVC相关的配置
<mvc:annotation-driven />
经过验证,这个是必须要配置的,因为它是和@RequestMapping结合使用的,这里再补充下与SpringMVC框架相关的知识点。
处理器映射器(HandlerMapping):用来处理用户请求的URL找到具体Controller的,有两种方式;
处理器适配器(HandlerAdapter): 用来处理具体Controller对应的具体方法的,有3种;
@RequestMapping这个注解的主要目的就是对具体的Controller类或者方法进行注册,以方便HandlerMapping用来处理请求的映射。但是@RequestMapping需要结合<mvc:annotation-driven />使用才能生效。
问题来源?
在实际的开发中,由于有了强大的注解功能,很多基于XML的配置方式已经被替代,但是在实际项目中,同时配置Spring和SpringMVC时会出现一些奇怪的异常,比如Bean被多次加载,多次实例化,或者依赖注入时,Bean不能被自动注入,但是明明你已经将该Bean注册了。
Spring配置文件applicationContext.xml,SpringMVC配置文件applicationContext-MVC.xml,这样项目中就有2个容器。
配置方式1:
applicationContext.xml中配置了<context:component-scan base-package=“com.it" />,负责所有需要注册的Bean的扫描工作。applicationContext-MVC.xml中配<mvc:annotation-driven />,负责springMVC相关注解的使用,启动项目发现,springMVC失效,无法进行跳转,进行调试,发现springMVC容器中的请求好像没有映射到具体controller中;
配置方式2:
将<context:component-scan base-package=“com.it" />和<mvc:annotation-driven />都配置在
applicationContext-MVC.xml中。Ok,可以运行。
为何?我用方式1.父容器进行扫描注册所有的bean,子容器负责使用相关的注解,难道不可以吗?查看源码发现的确不可以。
方式2却可以。原因是:
springMVC初始化时,会寻找所有当前容器中的所有@Controller注解的Bean,在方式1中,而当前容器springMVC容器中却没有注册的Bean。所有的@Controller配置的Bean都注册在Spring父容器中了。
What?难道就没有方式1的解决方案了吗?
答案是有的!在默认的情况下,子类容器不可以获得父类容器的Bean,当然我们可以在子类容器中配置使用父类的容器。
在applicationContext-MVC.xml中再配置如下的一段代码:
<property name="detectHandlerMethodsInAncestorContexts">
<value>true</value>
</property>
好了,前面的两种方式不是最完美的。
下面介绍第三种比较好的方式:
在实际工程中,会包括很多配置,根据不同的业务模块来划分,所以我们一般思路是各负其责,明确边界,Spring根容器负责所有其他非controller的Bean的注册,而SpringMVC只负责controller相关的Bean的注册。第三种方案如下:
Spring容器配置,排除所有@controller的Bean
<context:component-scan base-package="com.fsnip.open">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
SpringMVC容器配置,让其只包括@controller的Bean
<context:component-scan base-package="com.fsnip.open" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
引用一下参阅的博客的图片。 原图地址http://jinnianshilongnian.iteye.com/blog/1602617