1. 程式人生 > >Spring bean id 重複覆蓋的問題解決

Spring bean id 重複覆蓋的問題解決

問題提問:

 當我們的web應用龐大之後,裡面有很多的bean配置並且是分檔案的,如果兩個bean的配置id是一樣的而且實現類也是一樣的,例如有下面兩份xml的配置文件。

問題分析:

beancontext1.xml

<?xml version="1.0" encoding="UTF-8"?>  

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "/spring-beans.dtd">  

<beans>  

    <bean id="testbean" class="com.xxx.Bean">  

        <property name="name" value="beancontext1" />  

    </bean>  

</beans>  

beancontext2.xml

<?xml version="1.0" encoding="UTF-8"?>  

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "/spring-beans.dtd">  

<beans>  

    <bean id="testbean" class="com.xxx.Bean">  

        <property name="name" value="beancontext2" />  

    </bean>  

</beans>


當spring容器初始化時候同時載入這兩份配置檔案到當前的上下文的時候,程式碼如下:

public static void main(String[] args) {  

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(  

new String[] {  

"com/xxx/beancontext1.xml",  

"com/xxx/beancontext2.xml" }); 

//context.setAllowBeanDefinitionOverriding(false);  

//context.refresh();  

Bean bean = (Bean) context.getBean("testbean");  

System.out.println(bean.getName());  

}

執行這個程式你會看見控制檯上列印的結果是:

beancontext2

顯然,beancontext2.xml的bean的配置覆蓋了 beancontext1.xml中bean的配置,而且在spring初始化上下文的過程中這個過程是靜悄悄的執行的,連一點警告都沒有。這樣如果你的專案中定義了兩個id同名的bean,並且,他們的實現方式又是不一樣的,這樣在後期在專案中執行的邏輯看起來就會非常詭異,而且,如果有大量配置spring配置檔案的話,排查問題就會非常麻煩。

解決問題:

  那麼,我們如何來解決這個問題嗎?靠程式設計師自律?絕對不定義重複名稱的bean?我覺得這個是不靠譜的,只有通過在程式中引入一種交錯機制才能解決這個問題。

   首先,我們將上面那段程式的log4j日誌開啟,看看在spring在初始化的時候面對有兩個同名的bean是怎麼處理的。

- INFO - Refreshing org[email protected]aa9835: display name [org[email protected]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.xxx.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.xxx.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[email protected]aa9835]: org.s[email protected]1662dc8

- DEBUG - 1 beans defined in org[email protected]aa9835: display name [org[email protected]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 [[email protected]5f1]

- DEBUG - Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.[email protected]503429]

- INFO - Pre-instantiating singletons in org.s[email protected]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 這個類中有這樣一段程式碼:

synchronized (this.beanDefinitionMap) {  

    Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);  

    if (oldBeanDefinition != null) {  

        if (!this.allowBeanDefinitionOverriding) {  

            throw new BeanDefinitionStoreException(beanDefinition  

                    .getResourceDescription(), beanName,  

                    "Cannot register bean definition ["  

                            + beanDefinition + "] for bean '"  

                            + beanName + "': There is already ["  

                            + oldBeanDefinition + "] bound.");  

        } else {  

            if (this.logger.isInfoEnabled()) {  

                this.logger  

                        .info("Overriding bean definition for bean '"  

                                + beanName + "': replacing ["  

                                + oldBeanDefinition + "] with ["  

                                + beanDefinition + "]");  

            }  

        }  

    } else {  

        this.beanDefinitionNames.add(beanName);  

        this.frozenBeanDefinitionNames = null;  

    }  

    this.beanDefinitionMap.put(beanName, beanDefinition);  

    resetBeanDefinition(beanName);  

}

spring ioc容器在載入bean的過程中會去判斷beanName 是否有重複,如果發現重複的話在根據allowBeanDefinitionOverriding 這個成員變數,如果是true的話則丟擲BeanDefinitionStoreException 這個異常,如果為false的話就會覆蓋這個bean的定義。

所以,解決這個問題的辦法就比較簡單了,只要將這個allowBeanDefinitionOverriding值在spring初始化的時候設定為false就行了。

我把解決這個問題的環境放到,web工程中來:

在web工程中載入spring容器會通過:

<listener>  

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

</listener>

這個listener來完成的,在這個listener中會構造 org.springframework.web.context.ContextLoader 這個構造器來載入bean

所以,只要繼承擴充套件org.springframework.web.context.ContextLoaderListener這類就行了,程式碼如下:

解決方案:

CustomContextLoader:

import org.springframework.web.context.ConfigurableWebApplicationContext;  

import org.springframework.web.context.ContextLoader;  

import org.springframework.web.context.support.XmlWebApplicationContext;

import org.apache.commons.lang.BooleanUtils;

import org.apache.commons.lang.xwork.StringUtils;

public class CustomContextLoader extends ContextLoader {  

@Override  

protected void customizeContext(ServletContext servletContext,  

            ConfigurableWebApplicationContext applicationContext) {  

String allowBeanDefinitionOverriding  = servletContext.getInitParameter("AllowBeanDefinitionOverriding");

if(StringUtils.isNotBlank(allowBeanDefinitionOverriding)) {

   XmlWebApplicationContext context = (XmlWebApplicationContext) applicationContext;  

 context.setAllowBeanDefinitionOverriding(BooleanUtils.toBoolean(allowBeanDefinitionOverriding)); 

}

}

}

最後修改web.xml 檔案,修改listener的配置,如下:

<context-param>

<param-name>AllowBeanDefinitionOverriding</param-name>

<param-value>true|false</param-value>

</context-param>

<listener>  

 <listener-class>com.xxx.CustomContextLoaderListener</listener-class>  

</listener>

設定完這些就ok了,這樣你專案中如果在兩份被載入的xml檔案中如果再出現名字相同的bean的話,spring在載入過程中如果設定的是true就忽略覆蓋,如果是false就會無情的丟擲異常。ok!

相關推薦

Spring bean id 重複覆蓋的問題解決

問題提問:  當我們的web應用龐大之後,裡面有很多的bean配置並且是分檔案的,如果兩個bean的配置id是一樣的而且實現類也是一樣的,例如有下面兩份xml的配置文件。 問題分析: beancontext1.xml <?xml version="1.0"

spring bean id重複覆蓋的問題解決

問題:    當我們的web應用做成一個大專案之後,裡面有很多的bean配置,如果兩個bean的配置id是一樣的而且實現類也是一樣的,例如有下面兩份xml的配置文件: beancontext1.xml <?xml version="1.0" encoding="UTF

Spring-bean的迴圈依賴以及解決方式

版權宣告:本文為博主原創文章,未經博主允許不得轉載。    https://blog.csdn.net/u010853261/article/details/77940767 本文主要是分析Spring bean的迴圈依賴,以及Spring的解決方式。 通過這種解決方式,我們

DUBBO配置引發的一個問題--- DUPLICATE SPRING BEAN ID

1.原因    因專案業務需要,要呼叫RPC框架,專案原本已經依賴了很多RPC介面需要啟動時載入,所以準備做成啟動時不預載入。 就是在配置的時候加上check=false.   官方文件解釋的作用,就是Dubbo 預設會在啟動時檢查依賴的服務是否可用,不可用時會丟擲異常,阻止

Duplicate spring bean id BarterUserService

1.如果是在同一個專案下搞的測試,bean名就不要重複了,可能會出現這個錯誤 2.spring id 重複吧,或者載入了2個相同的配置檔案,可能會出現這個錯誤 3.web.xml中載入的時候

使用監聽器對Spring bean id進行唯一校驗

       因為Spring IOC容器啟動載入時會檢查bean定義是否有重複,如果有重複則會根據AbstractRefreshableApplicationContext類中的allowBeanDefinitionOverriding屬性值進行判斷,如果值為true,則把

Spring+Spring MVC+Mybatis整合配置AOP不生效的解決方案以及Bean初始化重複載入兩次(疑難雜症)

之前上班做spring+spring mvc +hibernate開發, 2年之久不做想複習一下aop的使用,結果配置遇見aop不生效,解決而記錄! 先上程式碼直接看反例效果會明顯: 首先看一下我的程式碼的包路徑: 接下來看Spring-MVC的配置檔案部分程式碼:

Spring Configuration動態繫結bean id

簡述: 對於bean id 可能在注入的時候需要根據配置動態的制定例項 程式碼: ERepositoryConfigure.java package com.cpa.components.system.e.repository; import org.apache.com

Spring Bean中迴圈依賴解決方案

在迴圈依賴是指在A中引用B,B中引用C,而C中引用A,容器建立物件時會出現死迴圈。相關解決方案如下: 1 選擇其一使其延遲載入,然後從上下文中獲取AService型別的bean即可。 現有AService 和BService,都在對方bean中注入,導致初始化時迴圈初始報錯,解決方案就是

Spring Bean 自動裝配 的歧義性(bean 的名稱重複)處理

在spring 中,spring 對於上下文的bean ,當自動裝配時,如果bean 的名稱相同,spring 無法做出選擇 。這就所謂的bean 自動裝配的歧義性。所以,當發現歧義性的時候,需要通過一些的方案來解決這個問題。 將可選bean 中的某個設定為首選(primary)的bea

關於quartz定時任務實現Job介面無法註解為spring bean 的一種解決方案

  通常情況下,我們使用quartz之後,定時任務實現Job介面,並重寫execute()方法: public class QuartzJob1 implements Job { /** * quartz回撥此介面,此介面中為定時任務具體執行內容 *

Quartz Job類無法注入spring bean問題解決方法

問題描述: 在Quartz的任務類中,無法使用autowired注入spring bean @Component @PersistJobDataAfterExecution @DisallowConcurrentExecution publi

Spring Bean 迴圈依賴解決方案

由於service層互相呼叫,一下子沒有什麼好的拆分方案,所以先解決迴圈依賴問題。 現有AService 和BService,都在對方bean中注入,導致初始化時迴圈初始報錯,解決方案就是選擇其一使其延遲載入。 用配置方式只要將其一設定lazy-init,具

spring 配置時 bean idbean name 的區別

簡單的說:id用來標識bean,是唯一的,且只有一個;name定義的是bean的alias,可以有多個,並可能與其他的bean重名。 詳細的說: id是唯一標識bean.不能用特殊字元:×#@ ,不能用數字開頭。在bean引用的時候只能用id指向你需要的bean;

javaEE 中spring bean 重複問題

第一:在典型的基於spring bean的javaEE專案中:webmvc-config.xml中: <context:component-scan base-package="com.jingoal" use-default-filters="false">

Spring-Bean的銷燬使用destroy-method()方法無效解決方案(容器!附原始碼)

Bean package com.gc.action; import java.util.Date; public class HelloWorld { private String msg=null;//該變數用來儲存字串 private Date date=

classpath 和 classpath* 區別以及如何覆蓋在配置檔案中的Bean ID

寫spring的程式碼到現在,一直都很習慣性的拷貝web.xml中的內容,沒怎麼在意裡面的內容,最近認真研究了下,很多東西都不是很理解,特別是classpath和classpath*的區別,研究了許久才搞明白,記錄下備忘。 classpath 和 classpath* 區別: classpath:只會到你

spring-boot bean 不被發現,解決方案簡述

搭建完spring boot的demo後自然要實現自動注入來體現spring ioc的便利了,但是我在實施過程中出現了這麼一個問題,見下面,這裡找到解決辦法記錄下來,供遇到同樣的問題的同僚參考 Description: Field helloService

Spring bean在不同情況下的預設id或name 侵立刪

轉自:https://www.cnblogs.com/1540340840qls/p/6962777.html bean如果不知名id是什麼它一般都有一個id或者講名字。第一種情況:元件掃描的情況:預設的id號或者bean的name是類名的首字母小寫。程式碼如下: 1 pac

spring bean重新載入問題解決

背景: 1、tomcat工程啟動比較慢,會導致很長的一段時間無法對外提供服務。 2、伺服器數量比較多,更新一次運維會花比較長的時間。 描述: 對系統框架進行調整,把會頻繁更新的功能,做成spring jar包,放在web站點,然後tomcat上部署的系統自動向web站點請