Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 424|回复: 0

spring bean id重复覆盖的问题解决

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-6-7 14:39:27 | 显示全部楼层 |阅读模式

    问题:

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

    beancontext1.xml

     

    [html]  view plain  copy
     
    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

     

     

    [html]  view plain  copy
     
    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容器初始化时候同时加载这两份配置文件到当前的上下文的时候,代码如下:

     

     

    [java]  view plain  copy
     
    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 这个类中有这样一段代码:

    [java]  view plain  copy
     
    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容器会通过:

     

    [html]  view plain  copy
     
    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:

    [java]  view plain  copy
     
    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.  *  
    8.  * @author 百岁(莫正华 baisui@taobao.com) 
    9.  * @version 1.0 2010-6-19 
    10.  */  
    11. public class KoubeiContextLoader extends ContextLoader {  
    12.   
    13.     @Override  
    14.     protected void customizeContext(ServletContext servletContext,  
    15.             ConfigurableWebApplicationContext applicationContext) {  
    16.         XmlWebApplicationContext context = (XmlWebApplicationContext) applicationContext;  
    17.         context.setAllowBeanDefinitionOverriding(false);  
    18.     }  
    19.   
    20. }  


    KoubeiContextLoaderListener:

     

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


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

     

     

    [html]  view plain  copy
     
    1. <listener>  
    2.         <listener-class>com.koubei.kac.springcontext.KoubeiContextLoaderListener</listener-class>  
    3. </listener>  


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

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-2 12:55 , Processed in 0.056932 second(s), 27 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表