不知道有沒有想到過我們的配置中約定的盡量一個(gè)bean使用一個(gè)name疆偿,可是隨著引入的第三方庫越來越多咱筛,就有可能就會出現(xiàn)名字相同的bean。那么問題來了杆故,多個(gè)name相同的bean,spring是如何處理的呢溉愁?是只保留第一個(gè)還是覆蓋保留最后一個(gè)又或者是拋出異常明確名稱不能重復(fù)处铛,又或者業(yè)務(wù)的因素明確了不能一致的情況?接下來我們看看這個(gè)同名bean的問題拐揭。
說起同名bean撤蟆,肯定要提到allowBeanDefinitionOverriding,這個(gè)關(guān)鍵字官方介紹是
Whether to allow re-registration of a different definition with the same name
,當(dāng)遇到同樣的名字的時(shí)候堂污,是否允許覆蓋注冊家肯。
把代碼定位到具體的位置 DefaultLiatableBeanFactory 文件
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
....
BeanDefinition oldBeanDefinition;
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
// 當(dāng)發(fā)現(xiàn)了重名的bean之后,而且不允許出現(xiàn)重名bean則拋出異常
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
// 用新的bean替換之前舊的bean
this.beanDefinitionMap.put(beanName, beanDefinition);
....
發(fā)現(xiàn)幾個(gè)疑問盟猖,可以實(shí)踐下
- 什么叫新的bean讨衣、如何界定
- 如何修改allowBeanDefinitionOverriding值
如何界定新的bean
已經(jīng)設(shè)置好了兩個(gè)xml,各自包含了一個(gè)同名的bean式镐,只是參數(shù)不一致而已
- 情況1
image.png
context1.xml 在前反镇, context.xml 在后
image.png
- 情況2
image.png
context.xml 在前, context1.xml 在后
image.png
看在讀取xml解析的時(shí)候同樣可以知道娘汞,答案已經(jīng)很明顯了歹茶,spring是按照文件列表一個(gè)一個(gè)掃描注冊的,所以最后保留的是后一個(gè)文件的bean
如何修改allowBeanDefinitionOverriding值
解決的思路肯定是要在真正的解析bean之前修改該值,而且要在defaultlistablebeanfactory生成之后惊豺。
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
// 已經(jīng)存在bean工廠燎孟,現(xiàn)在清空
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 創(chuàng)建bean工廠
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
// 自定義設(shè)入bean工廠的
loadBeanDefinitions(beanFactory);
// 開始加載xml進(jìn)行解析操作,無法修改bean工廠
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
// 把Application本身的是否可覆蓋bean值賦值給bean工廠
}
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
// 賦值是否允許循環(huán)引用
}
}
那么我們能夠動(dòng)刀的地方只能是customizeBeanFactory了,代碼如下
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,意味著不會主動(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());
}
如上圖的applicationContext.setAllowBeanDefinitionOverriding(false);
彻磁,這一步就是為了配合在refresh中的customizeBeanFactory函數(shù)操作了碍沐,這樣就完美的完成了我們的需求。
執(zhí)行結(jié)果衷蜓,如下圖累提,確實(shí)是提示錯(cuò)誤說在context1.xml 也是符合我們的預(yù)想