问题提问:
问题分析:
beancontext1.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "/spring-beans.dtd">
<beans>
</beans>
beancontext2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "/spring-beans.dtd">
<beans>
</beans>
当spring容器初始化时候同时加载这两份配置文件到当前的上下文的时候,代码如下:
public static void main(String[] args) {
ClassPathXmlApplicationC
new String[] {
"com/xxx/beancontext1.xml",
"com/xxx/beancontext2.xml" });
//context.setAllowBeanDefinitionOv
//context.refresh();
Bean bean = (Bean) context.getBean("testbean");
System.out.println(bean.getName());
}
执行这个程序你会看见控制台上打印的结果是:
beancontext2
显然,beancontext2.xml的bean的配置覆盖了 beancontext1.xml中bean的配置,而且在spring初始化上下文的过程中这个过程是静悄悄的执行的,连一点警告都没有。这样如果你的项目中定义了两个id同名的bean,并且,他们的实现方式又是不一样的,这样在后期在项目中执行的逻辑看起来就会非常诡异,而且,如果有大量配置spring配置文件的话,排查问题就会非常麻烦。
解决问题:
- INFO - Refreshing org.springframework.context.support.ClassPathXmlApplicationC
- 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.DocumentBuilderFactoryIm
- 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.DocumentBuilderFactoryIm
- 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.springframework.context.support.ClassPathXmlApplicationC
- DEBUG - 1 beans defined in org.springframework.context.support.ClassPathXmlApplicationC
- DEBUG - Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMessageSource@1cb25f1]
- DEBUG - Unable to locate ApplicationEventMulticas
- INFO - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFacto
- 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.DefaultListableBeanFacto
synchronized (this.beanDefinitionMap) {
}
spring ioc容器在加载bean的过程中会去判断beanName 是否有重复,如果发现重复的话在根据allowBeanDefinitionOverr
所以,解决这个问题的办法就比较简单了,只要将这个allowBeanDefinitionOverr
我把解决这个问题的环境放到,web工程中来:
在web工程中加载spring容器会通过:
<listener>
</listener>
这个listener来完成的,在这个listener中会构造 org.springframework.web.context.ContextLoader 这个构造器来加载bean
所以,只要继承扩展org.springframework.web.context.ContextLoaderListener这类就行了,代码如下:
解决方案:
CustomContextLoader:
import org.springframework.web.context.ConfigurableWebApplicati
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,
String allowBeanDefinitionOverr
if(StringUtils.isNotBlank(allowBeanDefinitionOverr
}
}
}
最后修改web.xml 文件,修改listener的配置,如下:
<context-param>
<param-name>AllowBeanDefinitionOverr
<param-value>true|false</param-value>
</context-param>
<listener>
<listener-class>com.xxx.CustomContextLoaderListe
</listener>
设置完这些就ok了,这样你项目中如果在两份被加载的xml文件中如果再出现名字相同的bean的话,spring在加载过程中如果设置的是true就忽略覆盖,如果是false就会无情的抛出异常。ok!