A bean with that name has already been defined in class path resource [path] and overriding is disabled
Asked Answered
V

6

29

I have the java configuration for the Spring Data Elaticsearch(using Transport Client) and ESTemplate. Here some except:

@Configuration
@EnableElasticsearchRepositories(basePackages = "subpackage-in-this-project")
@PropertySource("file:path-to-file")
public class ESConfig {

    @Bean
    ElasticsearchTemplate elasticsearchTemplate(Client client) {
        return new ElasticsearchTemplate(client);
    }

    @Bean
    Client client() { 
// configuration of the ES client
   }

}

And I have a config that extends the one above in the different project.

@Configuration
@ComponentScan("package-prefix-that-matches-packages-in-both-projects")
@EnableElasticsearchRepositories(basePackages = "subpackage-in-this-project")
@PropertySource("file:same-path-to-file-as-in-the-config-above")
public class ExtendedESConfig extends ESConfig {

    @Value("index-name")
    private String indexName;

    @Bean
    public String indexName() {
        return indexName;
    }
}

Upon executing a third Spring Boot application, which uses the dependency on the project with ExtendedESConfig, I get this and I can't quite understand why does it happen, started to happen after upgrading to 2.2.9.RELEASE from 2.0.5.RELEASE Spring Boot version.


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

Description:

The bean 'elasticsearchTemplate', defined in class path resource [my/package/ESConfig.class], could not be registered. A bean with that name has already been defined in class path resource [my/other/package/ExtendedESConfig.class] and overriding is disabled.

Action:

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

2020-08-30 16:49:46 ERROR [main] org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter:40 - 

Important remark from my comment:

... sadly, I am not the one who wrote this ES config and built the whole infrastructure around it. ...

At the time of this question, I don't own ExtendedESConfig nor can change it.

spring.main.allow-bean-definition-overriding=true is NOT an answer as I don't want to override bean declarations.

Vast answered 30/8, 2020 at 14:15 Comment(4)
Both config classes are annotated with @Configuration and one config extends another. So all beans in parent class will be created twice. Remove @Configuration from parent class and try. Also Its not a good practice to use inheritance in config classes.Needleful
@PratapiHemantPatel Thanks, I know that this is terrible, but, sadly, I am not the one who wrote this ES config and built the whole infrastructure around it. I gonna try that, but even if this helps this does spawn a number of other problems related to the fact that project with ES config is used standalone in a lot of the cases in too many places.Vast
If you own or can update ExtendedESConfig class then, you can use excludeFilter on @ComponentScan annotation to exclude ESConfig classNeedleful
I had a similar problem, and in my case the class definition had a @Service annotation. This effectively made it a @Bean as well which clashed with the @Bean definition in the @Configuration class. Removing @Service fixed the problem for me in Spring 2.6.3.Hibbard
A
17

Or you can add the next property to your application.properties :

spring.main.allow-bean-definition-overriding=true
Affined answered 2/12, 2021 at 16:50 Comment(1)
Not an answer. I didn't want to override beans, that is clear from the question. Also, this is obvious from the logs, even if I did want to override the beans configsVast
N
6

The default behaviour of overriding bean has been disabled in Spring Boot 2.1. Spring Boot 2.1 Release Notes

Since you don't own / or don't want to modify both configuration classes. You can exclude parent configuration form your SpringBootApplication class using @ComponentScan

@SpringBootApplication
@ComponentScan(excludeFilters = 
        {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = ESConfig.class)})
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
Needleful answered 31/8, 2020 at 2:51 Comment(3)
I want beans from the parent config to be included not excluded.Vast
Beans defined in Parent config will still be getting created since child config is still a configuration class. Excluding parent will allow parent beans to be created only once, not twice.Needleful
This is might be useful for some but this didn't answer my question.Vast
T
1

I had a similar problem with my custom springSecurityFilterChain method, in my @Configuration class. The application told me it couldn't create a Bean named springSecurityFilterChain since it was defined elsewhere (in my case, in a Spring Security package, which, as is your case, I couldn't modify).

I found the solution here and it amounted to simply changing my custom method's name; I chose customFilterChain. So it went from

@Bean
public SecurityFilterChain springSecurityFilterChain(HttpSecurity http) throws Exception {
    return http
            .csrf().disable()
            // etc
}

to:

@Bean
public SecurityFilterChain customFilterChain(HttpSecurity http) throws Exception {
    return http
            .csrf().disable()
            // etc
}

Amazingly it worked. Like the article says, Spring builds the bean using the method's name by default. Hope it helps.

Torre answered 5/2, 2023 at 17:54 Comment(1)
This is not an answer. This is just a workaround for the issue, not a solution you assume I don't know. And this workaround features Spring Security which is completely out of context here.Vast
A
0

I strongly recommend you debug the source code where the exception throw. As I know the exception throw from this function "org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition" in spring. I copy a slice of code below:

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
        throws BeanDefinitionStoreException {

    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");

    if (beanDefinition instanceof AbstractBeanDefinition) {
        try {
            ((AbstractBeanDefinition) beanDefinition).validate();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                    "Validation of bean definition failed", ex);
        }
    }

    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    if (existingDefinition != null) {
        if (!isAllowBeanDefinitionOverriding()) {
            throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
        }
        else if (existingDefinition.getRole() < beanDefinition.getRole()) {
            // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
            if (logger.isInfoEnabled()) {
                logger.info("Overriding user-defined bean definition for bean '" + beanName +
                        "' with a framework-generated bean definition: replacing [" +
                        existingDefinition + "] with [" + beanDefinition + "]");
            }
        }
        else if (!beanDefinition.equals(existingDefinition)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Overriding bean definition for bean '" + beanName +
                        "' with a different definition: replacing [" + existingDefinition +
                        "] with [" + beanDefinition + "]");
            }
        }
        else {
            if (logger.isTraceEnabled()) {
                logger.trace("Overriding bean definition for bean '" + beanName +
                        "' with an equivalent definition: replacing [" + existingDefinition +
                        "] with [" + beanDefinition + "]");
            }
        }
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }
    else {
        if (hasBeanCreationStarted()) {
            // Cannot modify startup-time collection elements anymore (for stable iteration)
            synchronized (this.beanDefinitionMap) {
                this.beanDefinitionMap.put(beanName, beanDefinition);
                List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                updatedDefinitions.addAll(this.beanDefinitionNames);
                updatedDefinitions.add(beanName);
                this.beanDefinitionNames = updatedDefinitions;
                removeManualSingletonName(beanName);
            }
        }
        else {
            // Still in startup registration phase
            this.beanDefinitionMap.put(beanName, beanDefinition);
            this.beanDefinitionNames.add(beanName);
            removeManualSingletonName(beanName);
        }
        this.frozenBeanDefinitionNames = null;
    }

    if (existingDefinition != null || containsSingleton(beanName)) {
        resetBeanDefinition(beanName);
    }
    else if (isConfigurationFrozen()) {
        clearByTypeCache();
    }
}

in above code you may found the exception BeanDefinitionOverrideException is exactly the root cause. When I set a break point at

BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);

You may found out all your beans are save in beanDefinitionMap, and if two bean has the same name, and the second bean called this map.get(beanName), it will retrieve the bean with the same name generate in pre process. and thus will trigger the exception!!

So now what you need to do is to compare this two bean. and found where did it come from. If it comes from xml or some Configuration code. and these is a debug skill you can paste the override bean name, in this case is "elasticsearchTemplate". But in my case the name is "org.springframework.cache.config.internalCacheAdvisor" like my exception shows like :

Description:The bean 'org.springframework.cache.config.internalCacheAdvisor', defined in class path resource [org/springframework/cache/annotation/ProxyCachingConfiguration.class], could not be registered. A bean with that name has already been defined and overriding is disabled. Action: Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

and it also ask me to "setting spring.main.allow-bean-definition-overriding=true" .

So set a break point at the line of map.get() code, then I paste the bean name in breakpoint condition:

beanName.equals("org.springframework.cache.config.internalCacheAdvisor")

if you don't know how to use debug condition, you can checkout this : How to use conditions in breakpoints in idea?

then finally I found out this breakpoint will trigger twice, as the second time when it triggered, it will throw the BeanDefinitionOverrideException.

as the bean with the same name has generate the second time. In my case, what I found out the root cause is the bean announced in both xml and again in spring default Configuration Code. But your case may not the same. So you need to use this debug tech to found the root cause!!

Hope this will help you, and as I know, don't open the override=true setting. it will get you more trouble than you thought!

Adrianople answered 6/2 at 3:53 Comment(0)
A
0

In my case, I've defined a redisTemplate Bean and also imported a 3rd-party starter for redission which caused this issue.

I've annotated a @AutoConfigureBefore({RedisAutoConfiguration.class, RedissonAutoConfigurationV2.class}) to resolve it.

Agonizing answered 16/5 at 2:25 Comment(0)
H
-6

find in your modul: resources/application.properties and write:

spring.main.allow-bean-definition-overriding=true

it help you, you need to enable the beans override mechanism.

Hypolimnion answered 16/9, 2022 at 10:42 Comment(1)
The duplicate of the existing non answerVast

© 2022 - 2024 — McMap. All rights reserved.