When using the spring-boot-configuration-processor and leveraging the same @ConfigurationProperties prefix on an @Bean I am seeing an APT error in Eclipse in 1.4.0.RELEASE. While I agree that this error should be generated when using a duplicate prefix on an @Component class definition, I think this restriction should be relaxed on @Bean definitions.

I am referencing the External Config 3rd Party Configuration section in the reference guide.

Basic example of the issue

@Bean
@ConfigurationProperties("spring.datasource.tomcat")
public DataSource readDataSource() {
        return DataSourceBuilder.create()
            .url(properties.getReadUrl())
            .username(properties.getUsername())
            .password(properties.getPassword())
            .build();
}

@Bean
@ConfigurationProperties("spring.datasource.tomcat")
public DataSource readWriteDataSource() {
        return DataSourceBuilder.create()
            .url(properties.getReadUrl())
            .username(properties.getUsername())
            .password(properties.getPassword())
            .build();
}
Duplicate `@ConfigurationProperties` definition for prefix 'spring.datasource.tomcat'.

Comment From: snicoll

I don't think it should. If we relax it as you ask us to do, it will generate the meta-data twice. Or are you implying that if the type and the prefix are the same we should "ignore" the second occurrence? I am not sure I want to do that.

Besides, this code of yours is a bit weird. You basically have two data sources that you configure from the same prefix. I'd rather have some kind of explicit pojo (a builder or something) to make it more clear.

Comment From: nwoike

The documentation describes this use case for "binding properties to third-party components that are outside of your control." So, for this usage, should metadata be generated when used on these types of @Bean definitions at all?

I am not advocating duplicating metadata generation. But I have several other usages of @ConfigurationProperties on my own classes that I do want to use the configuration processor on.

Comment From: nwoike

It may be a poor example, I just wanted to illustrate a use of dynamic property binding as used internally in Spring Boot's own Auto-configuration. As far as I know there is no @ConfigurationProperties bean or builder to configure DataSource connection pool properties.

Comment From: snicoll

should metadata be generated when used on these types of @Bean definitions at all?

Of course it should, you are writing "spring.datasource.tomcat.xyz" in your own config aren't you? The whole point of this is that you should get content assistance when you do. Binding two different bean definitions to the same prefix is a smell in my book. You could perfectly have "spring.datasource.tomcat.url" in your config and you would override the URL to use for both data sources. I am sure that's not what you want.

I have several other usages of @ConfigurationProperties on my own classes that I do want to use the configuration processor on.

Don't add the annotation processor to your build then. I'd argue that if you don't want the meta-data, something else is wrong. Ultimately it means you want to bind your bean to a certain prefix but you don't want content assistance for it.

as used internally in Spring Boot's own Auto-configuration.

Where? If you look how we're using it, we target different prefix. And for the datasource case if you look closer you'll see that the type of the DataSource is different as well (to generate the proper metadata per prefix).

Can we take a step back and look at what you're trying to solve and why you have two beans with the exact same signature?

Comment From: bclozel

@nwoike Sorry for asking this, but I still don't get the use case for this.

Are you trying to conditionally register beans depending on Profiles to avoid using application-read.properties and application-write.propreties? The example you gave is not clear and it's mixing many concerns.

Could you describe this use case in another way maybe? For example, what you have to write right now to achieve what you want and how that feature could help you make it more elegant/easier to write and maintain/etc.

Thanks

Comment From: nwoike

Hey I appreciate the discussion. I had previously hand written an additional-spring-configuration-metadata.json file and disabled the spring-boot-configuration-processor to move forward. I asked the question hoping I would learn a bit more and I did. I hadn't noticed that the metadata generator was generating metadata for Third-party classes like the Tomcat connection pool implementation in the example.

Thanks @bclozel but this is not related to multiple profile usage. In my scenario I have a Third-party class that I need multiple instances of but that I want to configure using the same property prefix due to the large number of configurable values (similar to the connection pool above). All instances must have the same property values applied and it makes it easier on our customers so they don't have to configure the same properties and values over using different prefixes.

Comment From: sxyandapp

我觉得楼主说的挺对的,@ConfigurationProperties注解本身的作用就是将特定的配置属性应用到某个springbean中,从这一点来讲,此注解本身不应该限定其prefix是否重复。 我当前遇到的问题就是,当我使用springboot配置多数据源时,我需要将hikari的相关配合应用到所有的数据源上,而且我希望所有数据源共享这些配置,因此我在每个数据源上都使用了@ConfigurationProperties("spring.datasource.hikari")这样的注解,然而springboot确提示我重复使用了@ConfigurationProperties,即使我将这两个注解写在了不同的类中 希望springboot官方能看到此issue,并修正此问题

Comment From: snicoll

Courtesy of Google translate

I think the landlord is quite right. The @ConfigurationProperties annotation itself is used to apply specific configuration properties to a springbean. From this point of view, the annotation itself should not limit whether its prefix is duplicated. The problem I am currently experiencing is that when I use springboot to configure multiple data sources, I need to apply hikari's related cooperation to all data sources, and I want all data sources to share these configurations, so I am at each data source. Both use the annotation @ConfigurationProperties("spring.datasource.hikari"), but springboot does prompt me to reuse @ConfigurationProperties, even though I wrote these two annotations in different classes. I hope springboot official can see this issue and fix this problem.

@sxyandapp A meta-data entry is related to a group and a group is related to a source. If we'd allowed this, you'd get the same keys duplicated over and over again (which matches your situation). You want the exact opposite (a single key that configures multiple groups). I'd rather have dedicated namespace (so that you can fine tune thing individually) and an EnvironmentPostProcessor that can map a "main namespace"

Comment From: wuwen5

In this case, what should we do ?

    @Bean
    @RefreshScope
    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    @ConditionalOnProperty(prefix = "spring.datasource.hikari", name = "refresh", havingValue = "true")
    DataSource dataSourceRefresh(DataSourceProperties properties) {
        System.setProperty("com.zaxxer.hikari.useWeakReferences", "true");
        HikariDataSource dataSource = properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
        if (StringUtils.hasText(properties.getName())) {
            dataSource.setPoolName(properties.getName());
        }
        return dataSource;
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    @ConditionalOnProperty(prefix = "spring.datasource.hikari", name = "refresh", havingValue = "false",
            matchIfMissing = true)
    DataSource dataSource(DataSourceProperties properties) {

        HikariDataSource dataSource = properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
        if (StringUtils.hasText(properties.getName())) {
            dataSource.setPoolName(properties.getName());
        }

        return dataSource;
    }

Comment From: wilkinsona

@wuwen5, I'd combine the two @Bean methods into one and use the Environment to check the value of the spring.datasource.hikari.refresh property and then set the Hikari system property as needed. If you have any further questions, please follow up on Stack Overflow or Gitter. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements.

Comment From: zlzzk0

How to handle this case

abstract class Base {
    var key: String

    abstract fun execute()
}



@ConfigurationProperties(prefix = "test")
fun getA() : Base {
    return object : Base {
        override fun execute() {
           // do something
        }
    }
}

@ConfigurationProperties(prefix = "test")
fun getB() : Base {
    return object : Base {
        override fun execute() {
           // do something else
        }
    }
}

properties.yml

test:
   key: value

Base class is a 3rd party lib

Comment From: wilkinsona

@zlzzk0 I would bind the properties once to a separate class and then use that to create and configure the instances of Base.

If you have any further questions, please follow up on Stack Overflow. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements.

Comment From: lz000

@zlzzk0 I would bind the properties once to a separate class and then use that to create and configure the instances of Base.

If you have any further questions, please follow up on Stack Overflow. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements.

I don't quite follow. Maybe an example? How would you configure Base with different instance of other class without making changes to the Base class? Note Base class is in a lib so not possible to change. My point is that there is still a need for supporting duplicate prefix.

Comment From: wilkinsona

You'd write some code that creates the Base instance and then calls its setter methods based on the properties that have been bound once to another class. You could use Spring Boot's PropertyMapper class to help with that. There are many usages of it in Spring Boot's code that you can search for to see examples.

As I said above, if you have any further questions, please follow up on Stack Overflow. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements.

Comment From: lz000

thanks @wilkinsona for your explanation. I'm trying to follow your idea but still could not figure out how this will work. But as you suggested I created a stackoverflow issue https://stackoverflow.com/questions/78570997/configurationproperties-and-duplicated-prefix. Happy to move the discussion over there. Based on your suggestion, I would need to create a Base instance, but it is an abstract class, we would have to create a child class first. No matter how we create child class, we always have to declare the ConfigurationProperties twice. The difference between those 2 child instances are the implementation of the abstract method. So does not seem this will resolve the issue.