Spring 中的父子容器是咋回事?

相信有小伙伴也听说过,在 SSM 项目中,Spring 容器是父容器,SpringMVC 是子容器,子容器可以访问父容器的 Bean,但是父容器不能访问子容器的 Bean。,更近一步,有小伙伴可能也了解过,不用父子容器,单纯就用一个 SpringMVC 容器似乎也可以,项目也能运行。,那么现在问题来了:既然单纯一个 SpringMVC 容器就能使项目跑起来,那我们为什么还要用父子容器?父子容器的优势是什么?,带着这个问题,今天松哥来和小伙伴们聊一聊父子容器。,首先,其实父子这种设计很常见,松哥记得在之前的 Spring Security 的系列文章中,Spring Security 中的 AuthenticationManager 其实也是类似的设计,估计那里就是借鉴了 Spring 中的父子容器设计。,当使用了父子容器之后,如果去父容器中查找 Bean,那么就单纯的在父容器中查找 Bean;如果是去子容器中查找 Bean,那么就会先在子容器中查找,找到了就返回,没找到则继续去父容器中查找,直到找到为止(把父容器都找完了还是没有的话,那就只能抛异常出来了)。,为什么需要父子容器?老老实实使用一个容器不行吗?,既然 Spring 容器中有父子容器,那么这个玩意就必然有其使用场景。,松哥举一个简单的例子。,假设我有一个多模块项目,其中有商家模块和客户模块,商家模块和客户模块中都有角色管理 RoleService,项目结构如下图:,现在 consumer 和 merchant 中都有一个 RoleService 类,然后在各自的配置文件中,都将该类注册到 Spring 容器中。,org.javaboy.consumer.RoleService:,org.javaboy.merchant.RoleService:,consumer_beans.xml 如下:,merchant_beans.xml 如下:,大家注意,这两个 Bean 同名。,现在,在 admin 模块中,同时依赖 consumer 和 merchant,同时加载这两个配置文件,那么能不能同时向 Spring 容器中注册两个来自不同模块的同名 Bean 呢?,代码如下:,这个执行之后会抛出如下问题:,图片,小伙伴们看到,这个是找不到 org.javaboy.consumer.RoleService 服务,但是另外一个 RoleService 其实是找到了,因为默认情况下后面定义的同名 Bean 把前面的覆盖了,所以有一个 Bean 就找不到了。,如果不允许 Bean 的覆盖,那么可以进行如下配置:,此时一启动就直接报错了:,图片图片,意思也说的比较明确了,Bean 的定义冲突了,所以定义失败。,那么有没有办法能够优雅的解决上面这个问题呢?答案就是父子容器!,对于上面的问题,我们可以将 consumer 和 merchant 配置成父子关系或者兄弟关系,就能很好的解决这个问题了。,先来看兄弟关系,代码如下:,小伙伴们看一下,这种针对 consumer 和 merchant 分别创建了容器,这种容器关系就是兄弟容器,这两个兄弟有一个共同的 parent 就是 ctx,现在可以在各个容器中获取到自己的 Bean 了。,需要注意的是,上面这种结构中,子容器可以获取到 parent 的 Bean,但是无法获取到兄弟容器的 Bean,即如果 consumer 中引用了 merchant 中的 Bean,那么上面这个配置就有问题了。,现在假设用 consumer 做 parent 容器,merchant 做 child 容器,那么配置如下:,首先创建两个容器,分别是 parent 和 child,然后为 child 容器设置 parent,设置完成后记得要刷新 child 容器。,现在我们就可以从 parent 容器中去获取 parent 容器中原本就存在的 Bean,也可以从 child 容器中去获取 child 容器原本的 Bean 或者是 parent 的 Bean 都可以。,这就是父子容器。,父容器和子容器本质上是相互隔离的两个不同的容器,所以允许同名的 Bean 存在。当子容器调用 getBean 方法去获取一个 Bean 的时候,如果当前容器没找到,就会去父容器查找,一直往上找,找到为止。,核心就是 BeanFactory,这个松哥之前文章已经和小伙伴们介绍过了(BeanFactoryPostProcessor 和 BeanPostProcessor 有什么区别?),BeanFactory 有一个子类 HierarchicalBeanFactory,看名字就是带有层级关系的 BeanFactory:,只要是 HierarchicalBeanFactory 的子类就能配置父子关系。父子关系图如下:,图片图片,需要注意的是,并不是所有的获取 Bean 的方法都支持父子关系查找,有的方法只能在当前容器中查找,并不会去父容器中查找:,如上,根据类型去查找 Bean 名称的时候,我们所用的是 getBeanNamesForType 方法,这个方法是由 ListableBeanFactory 接口提供的,而该接口和 HierarchicalBeanFactory 接口并无继承关系,所以 getBeanNamesForType 方法并不支持去父容器中查找 Bean,它只在当前容器中查找 Bean。,但是!如果你确实有需求,希望能够根据类型查找 Bean 名称,并且还能够自动去父容器中查找,那么可以使用 Spring 给我们提供的工具类,如下:,不过这个查找,对于父子容器中同名的 Bean 是查找不出来名字的。,上面的内容理解了,Spring 和 SpringMVC 之间的关系就好理解了,Spring 是父容器,SpringMVC 则是子容器。,在 SpringMVC 中,初始化 DispatcherServlet 的时候,会创建出 SpringMVC 容器,并且为 SpringMVC 容器设置 parent,相关代码如下:,FrameworkServlet#initWebApplicationContext:,这里的 rootContext 就是父容器,wac 就是子容器,无论哪种方式得到的子容器,都会尝试给其设置一个父容器。,如果我们在一个 Web 项目中,不单独配置 Spring 容器,直接配置 SpringMVC 容器,然后将所有的 Bean 全部都扫描到 SpringMVC 容器中,这样做是没有问题的,项目是可以正常运行的。但是一般项目中我们还是会把这两个容器分开,分开有如下几个好处:,另外再额外说一句,有的小伙伴可能会问,如果全部 Bean 都扫描到 Spring 容器中不用 SpringMVC 容器行不行?这其实也可以!但是需要一些额外的配置,这个松哥下篇文章再来和小伙伴们细述。,好啦,Spring 容器中的父子容器现在大家应该明白了吧?可以给非 ListableBeanFactory 容器设置父容器,父容器不可以访问子容器的 Bean,但是子容器可以访问父容器的 Bean。,

文章版权声明

 1 原创文章作者:cmcc,如若转载,请注明出处: https://www.52hwl.com/29419.html

 2 温馨提示:软件侵权请联系469472785#qq.com(三天内删除相关链接)资源失效请留言反馈

 3 下载提示:如遇蓝奏云无法访问,请修改lanzous(把s修改成x)

 免责声明:本站为个人博客,所有软件信息均来自网络 修改版软件,加群广告提示为修改者自留,非本站信息,注意鉴别

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023年6月23日
下一篇 2023年7月15日