问题:

当我们的web应用做成一个大项目之后,里面有很多的bean配置,如果两个bean的配置id是一样的而且实现类也是一样的,例如有下面两份xml的配置文档:

beancontext1.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "/spring-beans.dtd">
  3. <beans>
  4. <bean id="testbean" class="com.koubei.samebeannameconfict.Bean">
  5. <property name="name" value="beancontext1" />
  6. </bean>
  7. </beans>

beancontext2.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "/spring-beans.dtd">
  3. <beans>
  4. <bean id="testbean" class="com.koubei.samebeannameconfict.Bean">
  5. <property name="name" value="beancontext2" />
  6. </bean>
  7. </beans>

当spring容器初始化时候同时加载这两份配置文件到当前的上下文的时候,代码如下:

  1. public static void main(String[] args) {
  2. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
  3. new String[] {
  4. "com/koubei/samebeannameconfict/beancontext1.xml",
  5. "com/koubei/samebeannameconfict/beancontext2.xml" });
  6. //context.setAllowBeanDefinitionOverriding(false);
  7. //context.refresh();
  8. Bean bean = (Bean) context.getBean("testbean");
  9. System.out.println(bean.getName());
  10. }

执行这个程序你会看见控制台上打印的结果是:

beancontext2

显然,beancontext2.xml的bean的配置覆盖了 beancontext1.xml中bean的配置,而且在spring初始化上下文的过程中这个过程是静悄悄的执行的,连一点警告都没有。这样如果你的项目中定义了两个id同名的bean,并且,他们的实现方式又是不一样的,这样在后期在项目中执行的逻辑看起来就会非常诡异,而且,如果有大量配置spring配置文件的话,排查问题就会非常麻烦。

解决问题:

那么,我们如何来解决这个问题吗?靠程序员自律?绝对不定义重复名称的bean?我觉得这个是不靠谱的,只有通过在程序中引入一种交错机制才能解决这个问题。

首先,我们将上面那段程序的log4j日志打开,看看在spring在初始化的时候面对有两个同名的bean是怎么处理的。

- INFO - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@aa9835: display name [org.springframework.context.support.ClassPathXmlApplicationContext@aa9835]; startup date [Sat Jun 19 18:23:30 CST 2010]; root of context hierarchy
- INFO - Loading XML bean definitions from class path resource [com/koubei/samebeannameconfict/beancontext1.xml]
- DEBUG - Using JAXP provider [com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl]
- DEBUG - Found beans DTD [file:///spring-beans.dtd] in classpath: spring-beans.dtd
- DEBUG - Loading bean definitions
- DEBUG - Loaded 1 bean definitions from location pattern [com/koubei/samebeannameconfict/beancontext1.xml]
- INFO - Loading XML bean definitions from class path resource [com/koubei/samebeannameconfict/beancontext2.xml]
- DEBUG - Using JAXP provider [com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl]
- DEBUG - Found beans DTD [file:///spring-beans.dtd] in classpath: spring-beans.dtd
- DEBUG - Loading bean definitions
- INFO - Overriding bean definition for bean 'testbean': replacing [Generic bean: class [com.koubei.samebeannameconfict.Bean]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/koubei/samebeannameconfict/beancontext1.xml]] with [Generic bean: class [com.koubei.samebeannameconfict.Bean]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/koubei/samebeannameconfict/beancontext2.xml]]
- DEBUG - Loaded 0 bean definitions from location pattern [com/koubei/samebeannameconfict/beancontext2.xml]
- INFO - Bean factory for application context [org.springframework.context.support.ClassPathXmlApplicationContext@aa9835]:org.springframework.beans.factory.support.DefaultListableBeanFactory@1662dc8
- DEBUG - 1 beans defined in org.springframework.context.support.ClassPathXmlApplicationContext@aa9835: display name [org.springframework.context.support.ClassPathXmlApplicationContext@aa9835]; startup date [Sat Jun 19 18:23:30 CST 2010]; root of context hierarchy
- DEBUG - Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMessageSource@1cb25f1]
- DEBUG - Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.springframework.context.event.SimpleApplicationEventMulticaster@503429]
- INFO - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1662dc8: defining beans [testbean]; root of factory hierarchy
- DEBUG - Creating shared instance of singleton bean 'testbean'
- DEBUG - Creating instance of bean 'testbean'
- DEBUG - Eagerly caching bean 'testbean' to allow for resolving potential circular references
- DEBUG - Finished creating instance of bean 'testbean'
- DEBUG - Returning cached instance of singleton bean 'testbean'

以上日志中标红的是关键,spring在处理有重名的bean的定义的时候原来是使用的覆盖(override)的方式。我们来看看它是如何覆盖的

在org.springframework.beans.factory.support.DefaultListableBeanFactory 这个类中有这样一段代码:

  1. synchronized (this.beanDefinitionMap) {
  2. Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
  3. if (oldBeanDefinition != null) {
  4. if (!this.allowBeanDefinitionOverriding) {
  5. throw new BeanDefinitionStoreException(beanDefinition
  6. .getResourceDescription(), beanName,
  7. "Cannot register bean definition ["
  8. + beanDefinition + "] for bean '"
  9. + beanName + "': There is already ["
  10. + oldBeanDefinition + "] bound.");
  11. } else {
  12. if (this.logger.isInfoEnabled()) {
  13. this.logger
  14. .info("Overriding bean definition for bean '"
  15. + beanName + "': replacing ["
  16. + oldBeanDefinition + "] with ["
  17. + beanDefinition + "]");
  18. }
  19. }
  20. } else {
  21. this.beanDefinitionNames.add(beanName);
  22. this.frozenBeanDefinitionNames = null;
  23. }
  24. this.beanDefinitionMap.put(beanName, beanDefinition);
  25. resetBeanDefinition(beanName);
  26. }

spring ioc容器在加载bean的过程中会去判断beanName 是否有重复,如果发现重复的话在根据allowBeanDefinitionOverriding 这个成员变量,如果是true的话则抛出BeanDefinitionStoreException 这个异常,如果为false的话就会覆盖这个bean的定义。

所以,解决这个问题的办法就比较简单了,只要将这个allowBeanDefinitionOverriding值在spring初始化的时候设置为false就行了。

我把解决这个问题的环境放到,web工程中来:

在web工程中加载spring容器会通过:

  1. <listener>
  2. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  3. </listener>

这个listener来完成的,在这个listener中会构造 org.springframework.web.context.ContextLoader 这个构造器来加载bean

所以,只要扩展 ContextLoader 和ContextLoaderListener这两个类就行了,代码如下:

KoubeiContextLoader:

  1. import org.springframework.web.context.ConfigurableWebApplicationContext;
  2. import org.springframework.web.context.ContextLoader;
  3. import org.springframework.web.context.support.XmlWebApplicationContext;
  4. /**
  5. *
  6. *
  7. * @author 百岁(莫正华 baisui@taobao.com)
  8. * @version 1.0 2010-6-19
  9. */
  10. public class KoubeiContextLoader extends ContextLoader {
  11. @Override
  12. protected void customizeContext(ServletContext servletContext,
  13. ConfigurableWebApplicationContext applicationContext) {
  14. XmlWebApplicationContext context = (XmlWebApplicationContext) applicationContext;
  15. context.setAllowBeanDefinitionOverriding(false);
  16. }
  17. }

KoubeiContextLoaderListener:

  1. /**
  2. *
  3. *
  4. * @author 百岁(莫正华 baisui@taobao.com)
  5. * @version 1.0 2010-6-19
  6. */
  7. public class KoubeiContextLoaderListener extends ContextLoaderListener {
  8. @Override
  9. protected ContextLoader createContextLoader() {
  10. return new KoubeiContextLoader();
  11. }
  12. }

最后修改wen-inf 下web.xml 文件,修改listener的配置,如下:

  1. <listener>
  2. <listener-class>com.koubei.kac.springcontext.KoubeiContextLoaderListener</listener-class>
  3. </listener>

设置完这些就ok了,这样你项目中如果在两份被加载的xml文件中如果再出现名字相同的bean的话,spring在加载过程中就会无情的抛出异常,当你去除掉这个异常之后,就能重新出发了。yeah!!!!

spring bean id重复覆盖的问题解决的更多相关文章

  1. Duplicate spring bean id

    问题背景:从本地调用服务器的dubbo接口进行测试 实现思路:基于IDEA+Spring+maven+Dubbo搭建测试项目,从本地直接调用   具体实现思路可参考博客:https://www.cnb ...

  2. dubbo spring bean id冲突

    service-security-provider应用有provider和consumer配置文件 其中secutrity-consumer引用两个服务 <dubbo:reference int ...

  3. spring bean id和bean name的区别

    今天在分析问题时发现一个大家平时都不太注意的spring 配置问题,发出来分享下: 首先澄清一个概念: 同名bean:多个bean 有相同的 name 或者 id,称之为同名bean <bean ...

  4. Dubbo配置引发的一个问题--- Duplicate spring bean id

    1.原因 因项目业务需要,要调用RPC框架,项目原本已经依赖了很多RPC接口需要启动时加载,所以准备做成启动时不预加载. 就是在配置的时候加上check=false. 官方文档解释的作用,就是Dubb ...

  5. 【Spring】Spring bean中id和name的差异

    id和name都是spring 容器中中bean 的唯一标识符. id: 一个bean的唯一标识 , 命名格式必须符合XML ID属性的命名规范 name: 可以用特殊字符,并且一个bean可以用多个 ...

  6. spring bean的创建过程

    spring的核心容器包括:core.beans.context.express language四个模块.所以对于一个简单的spring工程,最基本的就是依赖以下三个jar包即可: <depe ...

  7. Spring JMX之一:使用JMX管理Spring Bean

    spring中关于jmx包括几个概念: MBeanExporter: 从字面上很容易理解, 用来将一些spring的bean作为MBean暴露给MBEanServer.MBeanServerFacto ...

  8. Spring学习(九)-----Spring bean配置继承

    在 Spring,继承是用为支持bean设置一个 bean 来分享共同的值,属性或配置. 一个子 bean 或继承的bean可以继承其父 bean 的配置,属性和一些属性.另外,子 Bean 允许覆盖 ...

  9. Spring bean配置继承

    在 Spring,继承是用为支持bean设置一个 bean 来分享共同的值,属性或配置. 一个子 bean 或继承的bean可以继承其父 bean 的配置,属性和一些属性.另外,子 Bean 允许覆盖 ...

随机推荐

  1. 在eclipse下使用maven的配置

    1 2 3 4 5 6 7 8 public class Shifty {     public static void main(String[] args) {         int i = 0 ...

  2. kolla all-in-one 安装

    http://docs.openstack.org/developer/kolla/ 使用了Docker containers and Ansible playbooks 目前在Fedora/Ubun ...

  3. 使用Mybatis连接数据库时报错:org.apache.ibatis.exceptions.PersistenceException: ### Error updating database.

    我的原因是字段名写错了,去数据库中复制字段名再运行就成功了.

  4. 在UIElement外面多套一层布局面板(Grid、StackPanel)的意义

    在一个UIElement或多个UIElement外面套上一层布局面板(Grid.StackPanel),可以起到统一管理作用(非重点关注):另外,更重要的是:可以起到扩大UIElement操作有效范围 ...

  5. 十 web爬虫讲解2—Scrapy框架爬虫—Scrapy安装—Scrapy指令

    Scrapy框架安装 1.首先,终端执行命令升级pip: python -m pip install --upgrade pip2.安装,wheel(建议网络安装) pip install wheel ...

  6. hive 权限:Authorization failed:No privilege 'Create' found for outputs .

    创建表报错: create table test ( name string ); Authorization failed:No privilege 'Create' found for outpu ...

  7. python----异常处理(二)

    格式如下: try:     pass except Exception as e:#如果上面出错,执行下面代码     pass else:#try如果没出错执行此下面代码     pass fin ...

  8. (转)程序员应该知道的10个eclipse调试技巧

    调试不仅可以查找到应用程序缺陷所在,还可以解决缺陷.对于Java程序员来说,他们不仅要学会如何在Eclipse里面开发像样的程序,更需要学会如何调试程序.本文介绍了Java程序员必知的10个调试技巧, ...

  9. hibernate映射(一对一、一对多、多对一、多对多)配置 【转】

    多对一关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是多指向一 一对多关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是一指向多 也就是说一对多和多对一的映射策略是一样的,只是站 ...

  10. SpringCloud教程 | 第十三篇: 断路器聚合监控(Hystrix Turbine)

    版权声明:本文为博主原创文章,欢迎转载,转载请注明作者.原文超链接 ,博主地址:http://blog.csdn.net/forezp. http://blog.csdn.net/forezp/art ...