Steve Johnson opened SPR-17649 and commented

Overview

I'm filing this report as suggested by @snicoll, who says this may be a bug.

I started a discussion because closed issue #13609 caused me a problem that I hadn't seen yet discussed in the context of the issue, or elsewhere.  Here is my original comment: 

[Context of discussion was change in behavior between Spring Boot 2.0 and Spring 2.1 where now overriding one bean def with another throws an exception, and the old behavior can be had with the property "spring.main.allow-bean-definition-overriding=true"]

This issue is causing a problem for me because I broke my single bean definition XML file into multiple files many months ago. I'm getting the error because I'm redefining THE SAME BEAN multiple times. This happens because of multiple XML files including the same other XML file to get the definitions of the beans it references from the other file. I do this so that can use different mixes of my bean definition files and be sure that the dependent bean definitions are always available for reference. It also helps in my IDE, where it doesn't have the context of one XML file being included after another, and so it thinks the latter file is missing definitions for referenced beans. What I am doing worked fine until 2.1.

My work-around is to set spring.main.allow-bean-definition-overriding=true. This fixes the problem. But I'd love to have the benefit of being warned if I'm truly overriding one bean with a DIFFERENT bean. I am going to look to see if I can reorganize my bean files to avoid the need to disable the check and still get the functionality I want, but it's a pain. I just wanted to put this particular problem on record. I'm not saying what I'm doing is a proper practice.

If your bean definition has an id the framework should not redefine the same bean definition multiple times, which should automatically fix your problem. If that does not fix it, please consider creating a Spring Framework issue with a small sample that reproduces the behavior you've described.

@inletfetch thanks for the feedback. It looks odd to me that the core container throws a BeanDefinitionOverrideException in such a case. Could you please create a Spring Framework issue?

Example

Per his request, I have put together an example.  It's the simplest case possible.  An XML file includes two XML files that each define a bean.  Those beans both depend on a common additional bean, so each of those files includes the same additional XML file, thus causing that XML file to be parsed twice.

Here are those files:

applicationContext.xml

<beans ...>
  <import resource="bean1a.xml"/>
  <import resource="bean1b.xml"/>
</beans>

bean1a.xml

<beans ...>
  <import resource="bean1.xml"/>
  <bean id="testBean1a" class="not.below.app.TestBean">
    <constructor-arg value="Hello From TestBean1a!!!"/>
    <constructor-arg ref="testBean1"/>
  </bean>
</beans>

bean1b.xml

<beans ...>
  <import resource="bean1.xml"/>
  <bean id="testBean1a" class="not.below.app.TestBean">
    <constructor-arg value="Hello From TestBean1a!!!"/>
    <constructor-arg ref="testBean1"/>
  </bean>
</beans> 

bean1.xml

<beans ...>
  <bean id="testBean1" class="not.below.app.TestBean">
    <constructor-arg value="Hello From TestBean1!!!"/>
  </bean>
</beans>

Stack Trace

And here's the error stream I get when I run my app:

2019-01-08 17:20:39.870 INFO 512 — [ main] hello.Application : Starting Application on US-ST032JO-MLT with PID 512 (/Users/steve/Development/inlet/inletfetch_if2/lab/gs-spring-boot/complete/target/classes started by steve in /Users/steve/Development/inlet/inletfetch_if2/lab/gs-spring-boot/complete)
 2019-01-08 17:20:39.872 INFO 512 — [ main] hello.Application : No active profile set, falling back to default profiles: default
 2019-01-08 17:20:40.222 WARN 512 — [ main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Failed to import bean definitions from URL location [classpath:bean1b.xml]
 Offending resource: URL [file:/Users/steve/Development/inlet/inletfetch_if2/lab/gs-spring-boot/complete/target/classes/applicationContext.xml|file:///Users/steve/Development/inlet/inletfetch_if2/lab/gs-spring-boot/complete/target/classes/applicationContext.xml]; nested exception is org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Failed to import bean definitions from relative location [bean1.xml]
 Offending resource: class path resource [bean1b.xml]; nested exception is org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Failed to register bean definition with name 'testBean1'
 Offending resource: class path resource [bean1.xml]; nested exception is org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'testBean1' defined in class path resource [bean1.xml]: Cannot register bean definition [Generic bean: class [not.below.app.TestBean]; scope=; 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 [bean1.xml]] for bean 'testBean1': There is already [Generic bean: class [not.below.app.TestBean]; scope=; 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 [bean1.xml]] bound.
 2019-01-08 17:20:40.232 INFO 512 — [ main] ConditionEvaluationReportLoggingListener :

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
 2019-01-08 17:20:40.234 ERROR 512 — [ main] o.s.b.d.LoggingFailureAnalysisReporter :

***************************
 APPLICATION FAILED TO START
 ***************************

Description:

The bean 'testBean1', defined in class path resource [bean1.xml], could not be registered. A bean with that name has already been defined in class path resource [bean1.xml] and overriding is disabled.

Action:

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

Now, I didn't get the "APPLICATION FAILED TO START" text before putting together my sample (don't know why...having a spring-boot-web in the mix maybe).  This text suggests that I should not expect to get away with defining a bean twice with the same name, even if it's the exact same bean.  If that's the case, in contrast to what @snicoll said, then maybe I've wasted my time (and that of others) in getting to this point. But...

I'd still like to suggest that if the SAME bean is being redefined...if the same code coordinates are being read a second time...it would be great if this could be ignored.  I've had my beans broken up this way for years.  There are multiple advantages we get by doing this.  I'm not sure I can do anything but turn off the checking for duplicate definitions.  I'd much rather have what I'm doing work, but still benefit by getting an error if I'm trying to define two DIFFERENT beans with the same id, or same class type in the case of autowiring or reference by class name.

(I've attached a complete, maven-buildable example)


Reference URL: https://github.com/spring-projects/spring-boot/issues/13609#issuecomment-452224306

Attachments: - complete.tgz (52.67 kB)

Comment From: pmv

Sorry, Steve, but I'm going to have to argue against what you are asking. Your issue happens because https://github.com/spring-projects/spring-framework/issues/5845 was never fixed. If the Spring maintainers want your scenario to work, loading each XML file only once seems like a much better solution.

As is, structuring a Spring project as you prefer can lead to major performance issues (a hole we're trying to dig out of by switching to java config). Therefore, I prefer the error message. Spring loads the bean twice, so it is an override, even though it's the same definition. If Spring would cache the fact that it processed the XML file (similar to how @Configuration files themselves are cached as beans), then it wouldn't be an override as the bean would only be created once.

I would very much like to see https://github.com/spring-projects/spring-framework/issues/5845 fixed after 13 years, and I believe that would also fix your issue. However, "fixing" this bug without addressing the underlying issue I'm (respectfully) very much against.

Comment From: biergit

Same thing can happen with Java config. For example if you want to use a library with a @EnableJpaRepositories(basePackages="com.acme") configuration and another library that contains a specific repository in a "com.acme" package and enables it with a @EnableJpaRepositories configuration.

Comment From: sbrannen

I am closing this issue, because it appears to have been superseded by #27978.

If you believe that #27978 will not cover your use cases, please provide additional comments here.