这篇文章主要介绍“如何解决Spring中配置id或name相同的Bean可能引发的问题”,在日常操作中,相信很多人在如何解决Spring中配置id或name相同的Bean可能引发的问题问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何解决Spring中配置id或name相同的Bean可能引发的问题”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
一、背景
如果再xml中配置了相同的<Bean>的ID或name可能会造成一些问题,今天我们来探讨一下并解决。
二、问题
1、在同一个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>
这种情况下,会直接抛出异常"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会直接覆盖text1.xml中的bean,Spring最终只会把text2.xml中的bean加载到IOC容器中。
此时spring并不会报错,只会打印info级别的日志信息,"Overriding bean definition for bean xxx with a different definition: replacing [xxx] with [xxx]" 这种情况下,要排查问题很困难。
三、解决
通过查看spring源码,我们发现在springIOC容器初始化时,有一个关键变量allowBeanDefinitionOverriding。
再来看下源码:
@Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { // 校验 beanName 与 beanDefinition 非空 Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); //校验解析的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); // 如果已经存在 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行,通过判断allowBeanDefinitionOverriding变量的值,来决定是覆盖还是抛出异常,而allowBeanDefinitionOverriding这个值默认是true。所以默认情况下是直接覆盖的,不会抛出异常。
那么我们很容易就想到,把allowBeanDefinitionOverriding的值改为false就可以解决问题。
查看源码,我们发现DefaultListableBeanFactory类提供了赋值allowBeanDefinitionOverriding变量的方法:
public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) { this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding; }
所以我们只要调用这个方法,把allowBeanDefinitionOverriding赋值成false就成功了。
那么如何修改呢? 我们来看下都有哪些类调用了setAllowBeanDefinitionOverriding()方法:
可以看到,在AbstractRefreshableApplicationContext类中调用了该方法, 我们继续跟进这个类中看:
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { if (this.allowBeanDefinitionOverriding != null) { beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.allowCircularReferences != null) { beanFactory.setAllowCircularReferences(this.allowCircularReferences); } }
发现是在customizeBeanFactory()方法中调用的,接着我们来跟踪this.allowBeanDefinitionOverriding变量,看看是在哪里设置的:
/** * 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()方法来设置allowBeanDefinitionOverriding变量的值。 直接在AbstractRefreshableApplicationContext这个类中查看哪里调用了这个方法,发现找不到, 那我们就看看他有哪些子类。
我们会发现一个非常熟悉的类:ClassPathXmlApplicationContext 接下来就简单了,我们只要通过ClassPathXmlApplicationContext类调用父类的setAllowBeanDefinitionOverriding()方法,就可以设置allowBeanDefinitionOverriding变量的值了。
代码如下:
public static void main(String[] args){ ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[]{"context.xml", "context1.xml"}, false); // 注意context的顺序,可以预知肯定是在context1.xml中出现冲突 // 注意这个false数据,设置为false,意味着不会主动的去刷新bean工厂以及解析xml applicationContext.setAllowBeanDefinitionOverriding(false); // 赋值application的参数allowBeanDefinitionOverriding applicationContext.refresh(); // 现在需要手动的启动refresh操作 Student student = (Student)applicationContext.getBean("student"); System.out.println(student.toString()); }
大功告成~
到此,关于“如何解决Spring中配置id或name相同的Bean可能引发的问题”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。