一、背景

最近由于项目的包扫描出现了问题,在解决问题的过程中,偶然发现了Spring和SpringMVC是有父子容器关系的,而且正是因为这个才往往会出现包扫描的问题,我们在此来分析和理解Spring和SpringMVC的父子容器关系并且给出Spring和SpringMVC配置文件中包扫描的官方推荐方式。

二、概念理解和知识铺垫

在Spring整体框架的核心概念中,容器是核心思想,就是用来管理Bean的整个生命周期的,而在一个项目中,容器不一定只有一个,Spring中可以包括多个容器,而且容器有上下层关系

目前最常见的一种场景就是在一个项目中引入Spring和SpringMVC这两个框架,那么它其实就是两个容器,Spring是父容器,SpringMVC是其子容器

并且在Spring父容器中注册的Bean对于SpringMVC容器中是可见的,而在SpringMVC容器中注册的Bean对于Spring父容器中是不可见的,也就是子容器可以看见父容器中的注册的Bean,反之就不行。

我们可以使用统一的如下注解配置来对Bean进行批量注册,而不需要再给每个Bean单独使用xml的方式进行配置。

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

从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 />

而上面这句配置Spring3.1之前的版本和以下配置方式等价

<!--配置注解控制器映射器,它是SpringMVC中用来将Request请求URL到映射到具体Controller--><bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/><!--配置注解控制器映射器,它是SpringMVC中用来将具体请求映射到具体方法--><bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>

Spring3.1之后的版本和以下配置方式等价

<!--配置注解控制器映射器,它是SpringMVC中用来将Request请求URL到映射到具体Controller--><bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/><!--配置注解控制器映射器,它是SpringMVC中用来将具体请求映射到具体方法--><bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

三、具体场景分析

下面让我们来详细扒一扒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找不到处理器,不能进行跳转。核心源码如下:

protected void initHandlerMethods() {  if (logger.isDebugEnabled()) {    logger.debug("Looking for request mappings in application context: " + getApplicationContext());  }  String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?        BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :       getApplicationContext().getBeanNamesForType(Object.class));  for (String beanName : beanNames) {    if (isHandler(getApplicationContext().getType(beanName))){      detectHandlerMethods(beanName);    }  }  handlerMethodsInitialized(getHandlerMethods());}

在方法isHandler中会判断当前bean的注解是否是controller,源码如下:

protected boolean isHandler(Class<?> beanType) {  return AnnotationUtils.findAnnotation(beanType, Controller.class) != null;}

而在第4步配置中,SpringMVC容器中也注册了所有带有@Controller注解的Bean,故SpringMVC能找到处理器进行处理,从而正常跳转。

我们找到了出现不能正确跳转的原因,那么它的解决办法是什么呢?

我们注意到在initHandlerMethods()方法中,detectHandlerMethodsInAncestorContexts这个Switch,它主要控制获取哪些容器中的bean以及是否包括父容器,默认是不包括的。

所以解决办法就是在SpringMVC的配置文件中配置HandlerMapping的detectHandlerMethodsInAncestorContexts属性为true即可(这里需要根据具体项目看使用的是哪种HandlerMapping),让它检测父容器的bean。如下:

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">   <property name="detectHandlerMethodsInAncestorContexts">      <value>true</value>  </property></bean>

但在实际工程中会包括很多配置,我们按照官方推荐根据不同的业务模块来划分不同容器中注册不同类型的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>

三、总结

这样我们在清楚了Spring和SpringMVC的父子容器关系、以及扫描注册的原理以后,根据官方建议我们就可以很好把不同类型的Bean分配到不同的容器中进行管理。再出现Bean找不到或者SpringMVC不能跳转以及事务的配置失效的问题,我们就可以很快的定位以及解决问题了。很开心,有木有~

转载于:https://blog.51cto.com/13919357/2299442

Spring和SpringMVC父子容器关系初窥相关推荐

  1. 探究Spring和SpringMVC父子容器关系

    兄弟萌,相信有很多人想不到 Spring 和 SpringMVC 是父子容器吧,^ - ^,下面我们来一探究竟. 一般做 SSM 框架项目时,扫描 @Controller 注解类的对象是在 Sprin ...

  2. 面试高频题:Spring和SpringMvc父子容器你能说清楚吗

    引言 以前写了几篇关于SpringBoot的文章<面试高频题:springBoot自动装配的原理你能说出来吗>.<保姆级教程,手把手教你实现一个SpringBoot的starter& ...

  3. Spring和springMVC父子容器的原理

    首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境: 其次,在web.xm ...

  4. Spring系列:父子容器详解

    又一次被面试官带到坑里面了. 面试官:springmvc用过么? 我:用过啊,经常用呢 面试官:springmvc中为什么需要用父子容器? 我:嗯...没听明白你说的什么. 面试官:就是control ...

  5. Spring MVC上下文父子容器

    2019独角兽企业重金招聘Python工程师标准>>> Spring MVC上下文父子容器 博客分类: java spring 在Spring MVC的启动依赖Spring框架,有时 ...

  6. Spring和SpringMVC的父子容器关系

    容器 在Spring整体框架的核心概念中,容器是核心思想 就是用来管理Bean的整个生命周期的 在一个项目中,容器不一定只有一个 Spring中可以包括多个容器,而且,容器有上下层关系 一个项目中引入 ...

  7. boot spring 没有父子容器_Spring 系列(二):Spring MVC的父子容器

    1.背景 在使用Spring MVC时候大部分同学都会定义两个配置文件,一个是Spring的配置文件spring.xml,另一个是Spring MVC的配置文件spring-mvc.xml. 在这里给 ...

  8. 简述Spring、SpringMVC、SpringBoot关系?

    1.Spring Spring是一个轻量级的Java开发框架,核心是SpringIOC(控制反转)和SpringAOP(面向切面),针对开发的web层.业务层.持久层等都提供了多种配置解决方案. 主要 ...

  9. Spring父子容器的关系分析--用实例说话

    Spring中父子容器的实现实例Spring的父子容器可以通过ConfigurableApplicationContext或ConfigurableBeanFactory来实现,这两个接口中分别有se ...

最新文章

  1. “火震”太多打乱NASA计划,火星探测计划将推迟到2022年底
  2. python正则表达式模块_Python正则表达式函数模块
  3. Java集合类ArrayList循环中删除特定元素
  4. php html采集,php file_get_contents函数轻松采集html数据
  5. Codeforces Round #112 (Div. 2) E. Compatible Numbers sosdp
  6. 壊小子的学习【日常阅读篇】(三)常规学习类图书
  7. vim快捷键使用记录
  8. Meteor 加入账户系统
  9. 李宏毅机器学习之Classification
  10. 如何快速导入SVN服务器的项目代码
  11. 二维数组的空间复杂度_剑指 offer 面试题精选图解 04 . 二维数组中的查找
  12. 牛客网暑期ACM多校训练营(第三场): E. Sort String(KMP)
  13. opnet之Aloha
  14. android蓝牙健康 iee,Wi-Fi RTT(IEEE 802.11mc)
  15. 艾肯6nano声卡安装调试教程
  16. 【转发】晶圆到底是什么?台积电为什么被称为晶圆厂?
  17. CentOS curl命令详解
  18. git使用meld的方法
  19. asp.net文件下载,实现隐藏文件下载地址
  20. cmd 删除文件和文件夹

热门文章

  1. Elasticsearch基础(五)搜索匹配
  2. python程序化 k线指定时间更新_Python获取股票历史、实时数据与更新到数据库
  3. python 提取元组中的值_Python中的sqlite3提取与元组中的值相对应的条目 - python
  4. nuxt打包路劲问题_简述Nuxt.js
  5. git如何合并指定文件内容_Git合并指定文件到另一个分支
  6. matlab课程设计实验,请大神帮我写一个数学实验的课程设计,用MATLAB,
  7. java reduce 分组_使用JAVA8 stream中三个参数的reduce方法对List进行分组统计
  8. 计算机六年级基础知识,六年级计算机试题
  9. 文件夹在哪里_在Mac电脑上截图和照片放在哪里?
  10. php 分页类 bootstrap,ThinkPHP分页使用bootstrap样式