在Spring整体框架的核心概念中,容器是核心思想,就是用来管理Bean的整个生命周期的,而在一个项目中,容器不一定只有一个,Spring中可以包括多个容器,而且容器有上下层关系,目前最常见的一种场景就是在一个项目中引入Spring和SpringMVC这两个框架,那么它其实就是两个容器,Spring是父容器,SpringMVC是其子容器,并且在Spring父容器中注册的Bean对于SpringMVC容器中是可见的,而在SpringMVC容器中注册的Bean对于Spring父容器中是不可见的,也就是子容器可以看见父容器中的注册的Bean,反之就不行。

springmvc 的配置文件和spring的配置文件都可以配置包扫描,如下:

<context:component-scan base-package="com.xxx"/>

从Spring提供的参考手册中我们得知该配置的功能是扫描配置的base-package包下的所有使用了@Component注解的类,并且将它们自动注册到容器中,同时也扫描@Controller,@Service,@Respository这三个注解,因为他们是继承自@Component。

在项目中我们经常见到还有如下这个配置,其实有了上面的配置,这个是可以省略掉的,因为上面的配置会默认打开以下配置。以下配置会默认声明了@Required、@Autowired、 @PostConstruct、@PersistenceContext、@Resource、@PreDestroy等注解。

<context:annotation-config/>

另外,还有一个和SpringMVC相关如下配置,经过验证,这个是SpringMVC必须要配置的,因为它声明了@RequestMapping、@RequestBody、@ResponseBody等。并且,该配置默认加载很多的参数绑定方法,比如json转换解析器等。

<mvc:annotation-driven />

下面让我们来详细扒一扒Spring与SpringMVC的容器冲突的原因到底在那里?

  我们共有Spring和SpringMVC两个容器,它们的配置文件分别为applicationContext.xml和applicationContext-MVC.xml。

  1.在applicationContext.xml中配置了<context:component-scan base-package=“com.hafiz.www" />,负责所有需要注册的Bean的扫描和注册工作。

  2.在applicationContext-MVC.xml中配置<mvc:annotation-driven />,负责SpringMVC相关注解的使用。

  3.启动项目我们发现SpringMVC无法进行跳转,将log的日志打印级别设置为DEBUG进行调试,发现SpringMVC容器中的请求好像没有映射到具体controller中。

  4.在applicationContext-MVC.xml中配置<context:component-scan base-package=“com.hafiz.www" />,重启后,验证成功,springMVC跳转有效。

下面我们来查看具体原因,翻看源码,从SpringMVC的DispatcherServlet开始往下找,我们发现SpringMVC初始化时,会寻找SpringMVC容器中的所有使用了@Controller注解的Bean,来确定其是否是一个handler。1,2两步的配置使得当前springMVC容器中并没有注册带有@Controller注解的Bean,而是把所有带有@Controller注解的Bean都注册在Spring这个父容器中了,所以springMVC找不到处理器,不能进行跳转。

按照官方推荐根据不同的业务模块来划分不同容器中注册不同类型的Bean:Spring父容器负责所有其他非@Controller注解的Bean的注册,而SpringMVC只负责@Controller注解的Bean的注册,使得他们各负其责、明确边界。配置方式如下

  1.在applicationContext.xml中配置:

<!-- Spring容器中注册非@controller注解的Bean -->
<context:component-scan base-package="com.hafiz.www">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

  2.applicationContext-MVC.xml中配置

<!-- SpringMVC容器中只注册带有@controller注解的Bean -->
<context:component-scan base-package="com.hafiz.www" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan> 

不要让两个扫描有重叠。原因有如下几点:

  1. 扫描的类增多, 项目启动时间会延长
  2. @PostConstruct 注解标注的方法会执行2次
  3. 会使事务失效

对于第三点会使事物失效,原因是什么? 
同时使用springmvc 和 spring, 那么项目中就会有两个容器。
spring的是父容器,先进行初始化; springmvc是子容器, 后进行初始化。子容器可以访问父容器的bean,父容器不能访问子容器的bean。 
springmvc后初始化,会重新创建service对象并重新注入,而springmvc再次创建service对象时不会读取spring的配置文件,因此也就无法知道service层是需要创建代理对象的,所以springmvc创建的service是普通的对象,而不是动态代理对象。

解决重叠扫描的方式

  • 方式1

即上面提到的 springmvc 只扫描controller的包, spring扫描其他组件。
springmvc 配置如下:

<context:component-scan base-package="com.xxx.**.cotroller"/> 

springmvc只会扫描com.xxx下任意目录或子目录下的controller包下的类

spring的配置如下:

<context:component-scan base-package="com.xxx.**.service.impl,com.xxx.**.dao"/>  
  • 方式2

用<context:include-filter/> 或 <context:exclude-filter/> 指定或排除某些类 
假设springmvc扫描如下:

<context:component-scan base-package="com.xxx.**.service.impl,com.xxx.**.dao"/> 

spring扫描如下:

<context:component-scan base-package="com.xxx"/>

那么就会重叠,springmvc会扫描service和dao,可以这样修改springmvc的配置:

<context:component-scan base-package="com.xxx">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context>

用 <context:exclude-filter/> 排除了@Service 和 @Repository 两种注解标注的类,意思是告诉springmvc在扫描时,如果碰到这两个注解标注的类直接忽略,不要创建和注入对象。

方式1比方式2好,扫描的类更少,方式2仅仅是个例子,看起来有点画蛇添足。故意让springmvc扫描service和dao的包,又告诉他扫描时忽略service和dao,我仅仅是想引出还有<context:include-filter/> 和 <context:exclude-filter/>  这个配置. 有些特殊场景才用得上这个配置。

参考资料

http://www.cnblogs.com/hafiz/p/5875740.html

Spring和Spring MVC包扫描的更多相关文章

  1. 为啥Spring和Spring MVC包扫描要分开?

    背景:       最近在搭建新工程的时候发现有些Spring的配置不是很了解,比如Spring 配置里面明明配置了component-scan,为啥Spring MVC配置文件还需要配置一下,这样岂 ...

  2. 为啥Spring和Spring MVC包扫描要分开

    开始学习springmvc各种小白问题 根据例子配置了spring扫描包,但是一直提示404错误,经过大量搜索,发现,扫描包的配置应该写在springmvc的配置文件中,而不是springmvc 配置 ...

  3. spring和springmvc包扫描问题

    写这篇博客之前,橘子松必须感慨下!!找了我一下午加一晚上(md),问了几个朋友也没找到.凉了啊 在搭建ssm之前,我把controller service mapper包扫描用基本包扫描   都写在a ...

  4. spring 排除指定的类或者包扫描

    <!-- 排除Controller注解的扫描 --> <context:component-scan base-package="exampleBean"> ...

  5. Spring IoC 源码分析 (基于注解) 之 包扫描

    在上篇文章Spring IoC 源码分析 (基于注解) 一我们分析到,我们通过AnnotationConfigApplicationContext类传入一个包路径启动Spring之后,会首先初始化包扫 ...

  6. 整合Spring时Service层为什么不做全局包扫描详解

    合Spring时Service层为什么不做全局包扫描详解 一.Spring和SpringMVC的父子容器关系 1.讲问题之前要先明白一个关系 一般来说,我们在整合Spring和SpringMVC这两个 ...

  7. 不会吧,有人用了两年Spring, 居然不知道包扫描是怎么实现的

    全栈的自我修养: 0004 Java 包扫描实现和应用(File篇) I may not be able to change the past, but I can learn from it. 我也 ...

  8. Spring源码分析-从@ComponentScan注解配置包扫描路径到IoC容器中的BeanDefinition,经历了什么(一)?

    阅前提醒 全文较长,建议沉下心来慢慢阅读,最好是打开Idea,点开Spring源码,跟着下文一步一步阅读,更加便于理解.由于笔者水平优先,编写时间仓促,文中难免会出现一些错误或者不准确的地方,恳请各位 ...

  9. spring盒springMVC整合父子容器问题:整合Spring时Service层为什么不做全局包扫描详解

    整合Spring时Service层为什么不做全局包扫描详解 一.Spring和SpringMVC的父子容器关系 1.讲问题之前要先明白一个关系 一般来说,我们在整合Spring和SpringMVC这两 ...

随机推荐

  1. 【Java并发编程四】关卡

    一.什么是关卡? 关卡类似于闭锁,它们都能阻塞一组线程,直到某些事件发生. 关卡和闭锁关键的不同在于,所有线程必须同时到达关卡点,才能继续处理.闭锁等待的是事件,关卡等待的是其他线程. 二.Cycli ...

  2. 《转载》Jenkins持续集成-自动化部署脚本的实现《python》

    本文转载自慕课网 读者须知:1.本手记本着记续接前面的两张手记内容整理2.本手记针对tomcat部署测试环境实现 最近工作比较繁忙,导致这章一直拖延,没有太抽出时间来总结.要实现Jenkins端的持续 ...

  3. myeclipse使用小技巧

    1 如何关掉maven自动更新,去掉前面的勾 2 myeclipse中如何设置不校验js

  4. it 删除远程分支

      一不小心把本地的临时分支push到server上去了,想要删除.一开始用git branch -r -d origin/branch-name不成功,发现只是删除的本地对该远程分支的track,正 ...

  5. 【Java知识点专项练习】之 Java鲁棒性的特点

    Java鲁棒性的特点如下: Java在编译和运行程序时都要对可能出现的问题进行检查,以防止错误的产生. Java编译器可以查出许多其他语言运行时才能发现的错误. Java不支持指针操作,大大减少了错误 ...

  6. c++ 类内static成员初始化

    类内部的static成员,除了为const static 且为整数类型(int char bool)可在类内部初始化. 其他的都建议在对应的cpp文件中进行初始化. test.h #ifndef TE ...

  7. [原]git的使用(五)---删除文件

    9.删除文件 [实践出真知] 创建test.txt 文件  并add 和commit到仓库 $ git status #新增加的文件test.txt On branch master Untracke ...

  8. 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十五:FIFO储存模块(同步)

    实验十五:FIFO储存模块(同步) 笔者虽然在实验十四曾解释储存模块,而且也演示奇怪的家伙,但是实验十四只是一场游戏而已.至于实验十五,笔者会稍微严肃一点,手动建立有规格的储存模块,即同步FIFO.那 ...

  9. iOS - Block产生Memory Leaks循环引用导致的内存泄漏以及解决方案

    在ARC(自动引用技术)前,Objective-c都是手动来分配释放 释放 计数内存,其过程非常复杂. ARC技术推出后,貌似世界和平了很多,但是其实ARC并不等同于Java或者C#中的垃圾回收,AR ...

  10. http get请求参数拼接

    localhost:8080/hbinterface/orderInterface/groupReverseAccept.do?bizType=4&&bnetAccount=ESBTE ...