1

I have the following Maven multi module setup with the application.properties and the corresponding properties class plus configuration in Module A:

@Component
@ConfigurationProperties(prefix = "city")
@Getter
@Setter
@ToString
public class CityProperties {
    private int populationAmountWorkshop;
    private double productionInefficientFactor;
    private Loaner loaner = new Loaner();
    private Tax tax = new Tax();
    private Guard pikeman = new Guard();
    private Guard bowman = new Guard();
    private Guard crossbowman = new Guard();
    private Guard musketeer = new Guard();

    @Getter
    @Setter
    @ToString
    public static class Loaner {
        private int maxRequest;
        private int maxAgeRequest;
        private int maxNbLoans;
    }

    @Getter
    @Setter
    @ToString
    public static class Tax {
        private double poor;
        private double middle;
        private double rich;
        private int baseHeadTax;
        private int basePropertyTax;
    }

    @Getter
    @Setter
    @ToString
    public static class Guard {
        private int weeklySalary;
    }
}

The application.properties:

#City
# Amount of inhabitants to warrant the city to have one workshop
city.populationAmountWorkshop=2500
# Factor that is applied on the efficient production to get the inefficient production
city.productionInefficientFactor=0.6
# Maximum requests per loaner
city.loaner.maxRequest=6
# Maximum  age of loan request in weeks
city.loaner.maxAgeRequest=4
# Maximum loan offers per loaner
city.loaner.maxNbLoans=3
# Weekly tax value factor for the various population per 100 citizens
city.tax.poor=0
city.tax.middle=0.6
city.tax.rich=2.0
city.tax.baseHeadTax=4
city.tax.basePropertyTax=280
city.pikeman.weeklySalary=3
city.bowman.weeklySalary=3
city.crossbowman.weeklySalary=4
city.musketeer.weeklySalary=6

And the Configuration

@Configuration
@ComponentScan(basePackageClasses = AConfiguration.class)
public class AConfiguration {
}

Then there is Module C with my application:

@SpringBootApplication
@Import({CConfiguration.class})
public class SpringBootApp {

    public static void main(String[] args) {
        SpringApplicationBuilder builder = new SpringApplicationBuilder(SpringBootApp.class);
        ApplicationContext context = builder.profiles("server").run();
        CityProperties cityProperties = context.getBean(CityProperties.class);
        System.out.println(cityProperties);
    }
}

The correlating Configuration:

@Configuration
@Import(AConfiguration.class)
@ComponentScan(basePackageClasses = {CConfiguration.class, DComponents.class})
@PropertySource("classpath:bean-test.properties")
public class CConfiguration {
    @Autowired
    @Qualifier("serverThreadPool")
    private Executor serverThreadPool;
    @Autowired
    private SubscriberExceptionHandler subscriptionExceptionHandler;

    @Bean
    public AsyncEventBus serverEventBus() {
        return new AsyncEventBus(serverThreadPool, subscriptionExceptionHandler);
    }

    @Bean
    @Primary
    @ConfigurationProperties(prefix = "city")
    public CityProperties cityProperties() {
        return new CityProperties();
    }
}

And the bean-test.properties file:

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

When removing the last bean from CConfiguration, then the application starts. However with that bean definition in place I get this error:

Description: The bean 'cityProperties', defined in class path resource [c/CConfiguration.class], could not be registered. A bean with that name has already been defined in file [/module1/target/classes/a/CityProperties.class] and overriding is disabled.

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

That is exactly what I defined in bean-test.properties. However that seems to be ignored. At first I thought that the properties file was not even loaded, but when misspelling it's name in PropertySource I get the error, that the file cannot be found on the classpath, which suggests that the file is picked up by Spring, but it's content is ignored.

hotzst
  • 7,238
  • 9
  • 41
  • 64
  • Possible duplicate of [How to set spring.main.allow-bean-definition-overriding to true in a Spring boot 2.1.0 starter configuration](https://stackoverflow.com/q/53450897/5221149) – Andreas Jan 01 '20 at 08:51
  • @Andreas that response suggests to use `AutoConfigureBefore` for my `CConfiguration`, but before what, because the other bean is picked up by the component scan defined on the same `Configuration` class. – hotzst Jan 01 '20 at 09:26
  • In addition, I was under the impression that having a `Primary` on the bean definition, that would be taken care of. – hotzst Jan 01 '20 at 09:58
  • `@Primary` in a bean definition makes it the *default* when auto-wiring by *type*. Beans still have to be [*uniquely named*](https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-beanname). If you want both beans to be loaded, with one of them the primary, then you need to rename one of them, e.g. [`@Bean("newName")`](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html#value--) or rename the method, since the method name is implicitly the bean name if not specified in the `@Bean` annotation. – Andreas Jan 01 '20 at 12:54
  • Bean definition overriding is about one bean *replacing* another bean of the *same name*. That's where `@AutoConfigureBefore/After` might become relevant, though it's intended to be used together with conditional annotations like `@ConditionalOnMissingBean`, so that only one bean definition is loaded, and hence no overriding takes place. For more information about bean definition overriding, I highly recommend that you read [**Spring Boot: Bean definition overriding**](https://brudenko.com/spring-bean-override). – Andreas Jan 01 '20 at 13:10

1 Answers1

1

I think that

@PropertySource("classpath:bean-test.properties")

will be used only for custom properties, not for spring properties. Try to put

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

in the application.properties.

alexej K
  • 152
  • 3
  • This woks, however the original use case for the problem is that the spring boot application is started in a unit test (not a spring boot test) and in my application code I do not allow the overriding of beans, but for testing it does make sense. – hotzst Jan 01 '20 at 12:40