38

Could someone give a MWE of how to use the @ConfigurationProperties annotation directly on a @Bean method?

I have seen countless examples of it being used on class definitions - but no examples yet for @Bean methods.

To quote the documentation:

  • Add this to a class definition or a @Bean method
  • @Target(value={TYPE,METHOD})

So, I think there is a possibility and an intended use as well - but unluckily I am unable to figure it out.

tmj
  • 1,750
  • 2
  • 19
  • 34

4 Answers4

57
spring.datasource.url = [url]
spring.datasource.username = [username]
spring.datasource.password = [password]
spring.datasource.driverClassName = oracle.jdbc.OracleDriver
@Bean
@ConfigurationProperties(prefix="spring.datasource")
public DataSource dataSource() {
    return new DataSource();
}

Here the DataSource class has properties url, username, password, driverClassName, so spring boot maps them to the created object.

Example of the DataSource class:

public class DataSource {
    private String url;
    private String driverClassName;
    private String username;
    private String password;
    //getters & setters, etc.
}

In other words this has the same effect as if you initialize some bean with stereotype annotations(@Component, @Service, etc.) e.g.

@Component
@ConfigurationProperties(prefix="spring.datasource")
public class DataSource {
    private String url;
    private String driverClassName;
    private String username;
    private String password;
    //getters & setters, etc.
}
moffeltje
  • 4,521
  • 4
  • 33
  • 57
Evgeni Dimitrov
  • 21,976
  • 33
  • 120
  • 145
  • 1
    thanks for the example. For more clarity could you also add the DataSource class – tmj Apr 05 '17 at 15:25
  • This is interesting - I do not see the point of the bean method now. Couldn't one directly `@Autowired` the DataSource class ? – tmj Apr 06 '17 at 11:37
  • 5
    @tMJ Imagine you have multiple database configurations. You could extend the `DataSource` class multiple times for each seperate database and add the `@ConfigurationProperties` to each one, Or you could create a bean for each one with the appropriate property prefix. e.g. (`@ConfigurationProperties(prefix="spring.datasource.test") @Bean public DataSource testDataSource(){...}`). This saves the redundancy of creating each class for the sake of seperate property sets. – coderatchet Sep 21 '17 at 06:58
  • @EvgeniDimitrov Thanks, it actually only occurred to me because I was looking for this very solution to my problem and this fit my current use case as described perfectly. – coderatchet Sep 21 '17 at 23:39
  • 1
    Just to point out that setters are important in the destination class (eg. Datasource). I thought it would use reflection for the mapping but it uses the setters instead. – nik686 Jan 09 '18 at 23:05
  • this is very powerful, since you can re-use the DataSource class for multiple configurations. – Vladimir Dec 14 '18 at 21:24
  • 1
    Starting form `spring-boot:2.2` class path scanning will automatically register beans of classes annotated with `@ConfigurationProperties` hence there is no need in `@Component` or `@EnableConfigurationProperty` for that matter. – rilaby Dec 19 '19 at 19:36
0

24.8.1 Third-party Configuration

As well as using @ConfigurationProperties to annotate a class, you can also use it on public @Bean methods. Doing so can be particularly useful when you want to bind properties to third-party components that are outside of your control.

To configure a bean from the Environment properties, add @ConfigurationProperties to its bean registration, as shown in the following example:

@ConfigurationProperties(prefix = "another")
@Bean
public AnotherComponent anotherComponent() {
    ...
}

Any property defined with the another prefix is mapped onto that AnotherComponent bean in manner similar to the preceding AcmeProperties example.

宏杰李
  • 11,820
  • 2
  • 28
  • 35
0

I found following solution: i.e. we have in application yaml couple of section and we are interesting in appConfig:

  appConfig:
  version: 1.0_alpha
  environment: ${spring.profiles}
  dbDriver: ${spring.datasource.driver-class-name}
  dbUrl: ${spring.datasource.url}
  keyCloak:
      serverOne:
          host: http://xx.xx.xxx.xxx:8080
          baseUrl: ${appConfig.keyCloak.serverOne.host}/auth/realms/master
          clientId: api-service-agent
          clientSecret: f00955443-d123-4cfe-90d3-e3ff3b214aaffe
          serviceUsername: service-user
          servicePassword: 1234567890
      serverTwo:
          host: http://xx.xxx.xxx.xxx:8080
          baseUrl: ${appConfig.keyCloak.serverTwo.host}/auth/realms/wissance
          clientId: api-service-agent
          clientSecret: a20ddf0-56fa-4991-85bc-114377eeffddcc
          serviceUsername: service-user
          servicePassword: 1234567890
      using: 
          baseUrl: ${appConfig.keyCloak.serverTwo.baseUrl}
          clientId: ${appConfig.keyCloak.serverTwo.clientId}
          clientSecret: ${appConfig.keyCloak.serverTwo.clientSecret}
          serviceUsername: ${appConfig.keyCloak.serverTwo.serviceUsername}
          servicePassword: ${appConfig.keyCloak.serverTwo.servicePassword}

We would like to split common settings and using KeyCloak settings, so i implemented following scheme:

I make following KeyCloakConfig class (without @ConfigurationProperties annotation) to store using authentication server settings:

@Configuration
public class KeyCloakConfig {

    public KeyCloakConfig(){

    }

    public KeyCloakConfig(String baseUrl, String clientId, String clientSecret, String username, String password) {
        this.baseUrl = baseUrl;
        this.clientId = clientId;
        this.clientSecret = clientSecret;
        this.username = username;
        this.password = password;
    }

    public String getBaseUrl(){
        return baseUrl;
    }

    public void setBaseUrl(String baseUrl){
        this.baseUrl = baseUrl;
    }

    public String getClientId(){
        return clientId;
    }

    public void setClientId(String clientId){
        this.clientId = clientId;
    }

    public String getClientSecret(){
        return clientSecret;
    }

    public void setClientSecret(String clientSecret){
        this.clientSecret = clientSecret;
    }

    public String getUsername(){
        return username;
    }

    public void setUsername(String username){
        this.username = username;
    }

    public String getPassword(){
        return password;
    }

    public void setPassword(String password){
        this.password = password;
    }

    @Value("${appConfig.keyCloak.using.baseUrl}")
    private String baseUrl;

    @Value("${appConfig.keyCloak.using.clientId}")
    private String clientId;

    @Value("${appConfig.keyCloak.using.clientSecret}")
    private String clientSecret;

    @Value("${appConfig.keyCloak.using.serviceUsername}")
    private String username;

    @Value("${appConfig.keyCloak.using.servicePassword}")
    private String password;
}

and AppConfig class that holds common settings like version, environment using DB driver & url and also KeyCloakConfig as property:

@Configuration
@EnableConfigurationProperties
@ConfigurationProperties
public class AppConfig {

public AppConfig(){

    }

    public AppConfig(String apiVersion, String environment, String databaseDriver, String databaseUrl){
        this.apiVersion = apiVersion;
        this.environment = environment;
        this.databaseDriver = databaseDriver;
        this.databaseUrl = databaseUrl;
    }

    public String getEnvironment(){
        return environment;
    }

    public void setEnvironment(String environment) {
        this.environment = environment;
    }

    public String getDatabaseDriver(){
        return databaseDriver;
    }

    public void setDatabaseDriver(String databaseDriver) {
        this.databaseDriver = databaseDriver;
    }

    public String getDatabaseUrl(){
        return databaseUrl;
    }

    public void setDatabaseUrl(String databaseUrl) {
        this.databaseUrl = databaseUrl;
    }

    public String getApiVersion(){
        return apiVersion;
    }

    public void setApiVersion(String apiVersion) {
        this.apiVersion = apiVersion;
    }

    public KeyCloakConfig getKeyCloakConfig(){
        return keyCloakConfig;
    }

    public void setKeyCloakConfig(KeyCloakConfig keyCloakConfig){
        this.keyCloakConfig = keyCloakConfig;
    }

    @Value("${appConfig.version}")
    private String apiVersion;

    @Value("${appConfig.environment}")
    private String environment;

    @Value("${appConfig.dbDriver}")
    private String databaseDriver;

    @Value("${appConfig.dbUrl}")
    private String databaseUrl;

    @Autowired
    private KeyCloakConfig keyCloakConfig;
}
Michael Ushakov
  • 1,639
  • 1
  • 10
  • 18
-2

You can use @ConfigurationProperties as below

Entity Model

public class MY_ENTITY {
    private String prop1;
    private String prop2;
    // setter & getter & toString()
}

Bean Method

@Configuration
public class MyClass {

    @Bean
    @ConfigurationProperties(prefix = "my.entity")
    public MY_ENTITY getContract() {
        return new MY_ENTITY()
                .setProp1("prop1111111")
                .setProp2("prop2222222")
                ;
    }

    @Bean(name = "contract2")
    @ConfigurationProperties(prefix = "my.entity2")
    public MY_ENTITY getContract2() {
        return new MY_ENTITY()
                .setProp1("prop1111.2222")
                .setProp2("prop2222.222")
                ;
    }
}

application.properties

my.entity.prop1=2120180023
my.entity.prop2=CUSTOMER_NAME111

my.entity2.prop1=9994494949
my.entity2.prop2=CUSTOMER_NAME222

SpringBootApplication

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {

    @Autowired
    @Qualifier("contract2")
    private MY_ENTITY myEntity;

    public static void main(String[] args) throws Exception {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        System.out.println(myEntity);
    }
}
KevinBui
  • 880
  • 15
  • 27