EN VI

Java - Avoiding Bean name conflicts from Spring Boot imported library?

2024-03-12 18:30:06
How to Java - Avoiding Bean name conflicts from Spring Boot imported library

I'm developing a Maven library designed to be used in a Spring Boot application. This library internally defines couple of beans, which are loaded into the Spring context through Spring's auto-configuration mechanism. Below is the structure and key files of the library:

Library Structure:

-the-library
|  |-src
|  .   |-main
|  .   .  |-java
|  .   .  .  |-com.example.common
|  .   .  .  .  |-FooModuleConfiguration.java
|  .   .  .  .  |-ClockBean.java
|  .   .  |-resources
|  .   .  .  |-META-INF.spring
|  .   .  .  .  |-org.springframework.boot.autoconfigure.AutoConfiguration.imports
|  |-pom.xml

In my FooModuleConfiguration.java I have:

package com.example.common;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
public class FooModuleConfiguration {
    
}

In my ClockBean.java I have:

package com.example.common;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Clock;

@Configuration
public class ClockBean {

    @Bean
    @ConditionalOnMissingBean(Clock.class)
    public Clock clock() {
        return Clock.systemDefaultZone();
    }
}
 
}

And in my org.springframework.boot.autoconfigure.AutoConfiguration.imports I have:

com.example.common.FooModuleConfiguration

The problem is when I try to define similar ClockBean in a project (let's call it Bar) that uses the above library. This results in a name clash, leading to the following exception:

org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [com.example.bar.BarApplication]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'clockBean' for bean class [com.example.common.ClockBean] conflicts with existing, non-compatible bean definition of same name and class [com.example.bar.ClockBean]

I know that I can manually change the class or Bean name in one of the projects, but I'm looking for a more generic sulution. Is there a way to automatically prefix each bean defined in the library or to modify the bean name generation process in such a manner that it affects only the beans within the library (or within defined package)? Or must I resort to using complex class names within the library (like FooCommonBeanConfig.java) to minimize the risk of name conflicts?

Solution:

I've used BeanNameGenerator for this.


public class PrefixingBeanNameGenerator extends AnnotationBeanNameGenerator {
    private final String prefix;

    public PrefixingBeanNameGenerator(String prefix) {
        this.prefix = prefix;
    }

    @Override
    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        return prefix + super.generateBeanName(definition, registry);
    }
}

@Configuration
@ComponentScan(basePackages = "com.yourlibrary", nameGenerator = PrefixingBeanNameGenerator.class)
public class PrefixingBeanNameConfig {

    public PrefixingBeanNameGenerator beanNameGenerator() {
        return new PrefixingBeanNameGenerator("yourPrefix_");
    }
}

Answer

Login


Forgot Your Password?

Create Account


Lost your password? Please enter your email address. You will receive a link to create a new password.

Reset Password

Back to login