您好,登錄后才能下訂單哦!
這篇文章主要介紹“如何解決Spring中配置id或name相同的Bean可能引發(fā)的問(wèn)題”,在日常操作中,相信很多人在如何解決Spring中配置id或name相同的Bean可能引發(fā)的問(wèn)題問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”如何解決Spring中配置id或name相同的Bean可能引發(fā)的問(wèn)題”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
一、背景
如果再xml中配置了相同的<Bean>的ID或name可能會(huì)造成一些問(wèn)題,今天我們來(lái)探討一下并解決。
二、問(wèn)題
1、在同一個(gè)xml中配置了相同的bean的id。EX:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "/spring-beans.dtd"> <beans> <bean id="test" class="com.xxx.Bean"> <property name="name" value="111" /> </bean> </beans> <beans> <bean id="test" class="com.xxx.Bean"> <property name="name" value="222" /> </bean> </beans>
這種情況下,會(huì)直接拋出異常"Cannot register bean definition [xxx] for bean xxx: There is already [xxx] bound."
2、在不同的xml中配置相同的bean的id。EX:
test1.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "/spring-beans.dtd"> <beans> <bean id="test" class="com.xxx.Bean"> <property name="name" value="111" /> </bean> </beans>
test2.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "/spring-beans.dtd"> <beans> <bean id="test" class="com.xxx.Bean"> <property name="name" value="222" /> </bean> </beans>
這種情況下text2.xml中的bean會(huì)直接覆蓋text1.xml中的bean,Spring最終只會(huì)把text2.xml中的bean加載到IOC容器中。
此時(shí)spring并不會(huì)報(bào)錯(cuò),只會(huì)打印info級(jí)別的日志信息,"Overriding bean definition for bean xxx with a different definition: replacing [xxx] with [xxx]" 這種情況下,要排查問(wèn)題很困難。
三、解決
通過(guò)查看spring源碼,我們發(fā)現(xiàn)在springIOC容器初始化時(shí),有一個(gè)關(guān)鍵變量allowBeanDefinitionOverriding。
再來(lái)看下源碼:
@Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { // 校驗(yàn) beanName 與 beanDefinition 非空 Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); //校驗(yàn)解析的BeanDefiniton if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } BeanDefinition oldBeanDefinition; // 從緩存中獲取指定 beanName 的 BeanDefinition oldBeanDefinition = this.beanDefinitionMap.get(beanName); // 如果已經(jīng)存在 if (oldBeanDefinition != null) { // 如果存在但是不允許覆蓋,拋出異常 if (!isAllowBeanDefinitionOverriding()) {------------------6 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound."); } // 覆蓋 beanDefinition 大于 被覆蓋的 beanDefinition 的 ROLE ,打印 info 日志 else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (this.logger.isWarnEnabled()) { this.logger.warn("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(oldBeanDefinition)) { if (this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else { if (this.logger.isDebugEnabled()) { this.logger.debug("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } // 允許覆蓋,直接覆蓋原有的 BeanDefinition 到 beanDefinitionMap 中。 this.beanDefinitionMap.put(beanName, beanDefinition); }
可以看到在第6行,通過(guò)判斷allowBeanDefinitionOverriding變量的值,來(lái)決定是覆蓋還是拋出異常,而allowBeanDefinitionOverriding這個(gè)值默認(rèn)是true。所以默認(rèn)情況下是直接覆蓋的,不會(huì)拋出異常。
那么我們很容易就想到,把a(bǔ)llowBeanDefinitionOverriding的值改為false就可以解決問(wèn)題。
查看源碼,我們發(fā)現(xiàn)DefaultListableBeanFactory類提供了賦值allowBeanDefinitionOverriding變量的方法:
public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) { this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding; }
所以我們只要調(diào)用這個(gè)方法,把a(bǔ)llowBeanDefinitionOverriding賦值成false就成功了。
那么如何修改呢? 我們來(lái)看下都有哪些類調(diào)用了setAllowBeanDefinitionOverriding()方法:
可以看到,在AbstractRefreshableApplicationContext類中調(diào)用了該方法, 我們繼續(xù)跟進(jìn)這個(gè)類中看:
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { if (this.allowBeanDefinitionOverriding != null) { beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.allowCircularReferences != null) { beanFactory.setAllowCircularReferences(this.allowCircularReferences); } }
發(fā)現(xiàn)是在customizeBeanFactory()方法中調(diào)用的,接著我們來(lái)跟蹤this.allowBeanDefinitionOverriding變量,看看是在哪里設(shè)置的:
/** * Set whether it should be allowed to override bean definitions by registering * a different definition with the same name, automatically replacing the former. * If not, an exception will be thrown. Default is "true". * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowBeanDefinitionOverriding */ public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) { this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding; }
可以看到AbstractRefreshableApplicationContext暴露了setAllowBeanDefinitionOverriding()方法來(lái)設(shè)置allowBeanDefinitionOverriding變量的值。 直接在AbstractRefreshableApplicationContext這個(gè)類中查看哪里調(diào)用了這個(gè)方法,發(fā)現(xiàn)找不到, 那我們就看看他有哪些子類。
我們會(huì)發(fā)現(xiàn)一個(gè)非常熟悉的類:ClassPathXmlApplicationContext 接下來(lái)就簡(jiǎn)單了,我們只要通過(guò)ClassPathXmlApplicationContext類調(diào)用父類的setAllowBeanDefinitionOverriding()方法,就可以設(shè)置allowBeanDefinitionOverriding變量的值了。
代碼如下:
public static void main(String[] args){ ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[]{"context.xml", "context1.xml"}, false); // 注意context的順序,可以預(yù)知肯定是在context1.xml中出現(xiàn)沖突 // 注意這個(gè)false數(shù)據(jù),設(shè)置為false,意味著不會(huì)主動(dòng)的去刷新bean工廠以及解析xml applicationContext.setAllowBeanDefinitionOverriding(false); // 賦值application的參數(shù)allowBeanDefinitionOverriding applicationContext.refresh(); // 現(xiàn)在需要手動(dòng)的啟動(dòng)refresh操作 Student student = (Student)applicationContext.getBean("student"); System.out.println(student.toString()); }
大功告成~
到此,關(guān)于“如何解決Spring中配置id或name相同的Bean可能引發(fā)的問(wèn)題”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。