问题:

当我们的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. Author Agreement

    Dear Editor,We the undersigned declare that this manuscript entitled “文章标题” is original, has not bee ...

  2. LeetCode第[50]题(Java):Pow(x, n)

    题目:求x的n次幂 难度:Medium 题目内容: Implement pow(x, n), which calculates x raised to the power n (xn). 翻译: 实现 ...

  3. mysql建立索引

    mysql的几种索引http://jingyan.baidu.com/article/da1091fbd166ff027849d687.html

  4. hash路由(哈希路由)

    1.https://www.cnblogs.com/huanying2015/p/8047376.html (js 哈希路由原理实现) 2.https://www.cnblogs.com/yeer/a ...

  5. Activity的基本概念与Activity的生命周期

    一.Activity的基本概念 Activity是Android的四大组件之一,它是一种可以包含用户界面的组件,主要用于和用户进行交互,比如打电话,照相,发送邮件,或者显示一个地图!Activity用 ...

  6. LeetCode OJ:Palindrome Linked List(回文链表判断)

    Given a singly linked list, determine if it is a palindrome. Follow up:Could you do it in O(n) time ...

  7. NSPredicate(正则表达式)

    1. 正则表达式使用单个字符串来描述.匹配一系列符合某个句法规则的字符串.通常被用来检索.替换那些符合某个模式的文本. 2. iOS中正则使用 有三种(NSPredicate, rangeOfStri ...

  8. VS2010 将程序发布至网站时,发生错误“未能给 bin/Debug/publish//setup.exe 签名“

    VS2010 将程序发布至网站时,发生错误“未能给 bin/Debug/publish//setup.exe 签名“  错误:   因为某项目未能生成,所以无法发布. ---------------- ...

  9. pg limit限制返回的行

    limit 20:返回结果集中的前20行 offset 20 limit 20:返回结果集中前40行中的后20行 示例: 创建测试表: postgres=# create table test_lim ...

  10. 兼容 数组 api map代码

    if(!("map" in Array.prototype)) Array.prototype.map=function(fun){ for(var i=0,arr=[]; i&l ...