简体   繁体   中英

@ConfigurationProperties prefix not working

.yml file

cassandra:
    keyspaceApp:junit
solr:
    keyspaceApp:xyz

Bean

@Component
@ConfigurationProperties(prefix="cassandra")
public class CassandraClientNew {
   @Value("${keyspaceApp:@null}") private String keyspaceApp;

Main method file

@EnableAutoConfiguration
@ComponentScan
@PropertySource("application.yml")
public class CommonDataApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(CommonDataApplication.class)
                .web(false).headless(true).main(CommonDataApplication.class).run(args);
    }
}

TestCase

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = CommonDataApplication.class)
@IntegrationTest
@EnableConfigurationProperties
public class CassandraClientTest {

    @Autowired
    CassandraClientNew cassandraClientNew;

    @Test
    public void test(){
        cassandraClientNew.getSession();
        System.out.println(" **** done ****");
    }
}

Instead of setting junit as the keyspaceApp it sets xyz.

Looks like prefix="cassandra" not working

It looks like you are trying to use Spring Boot Typesafe Configuration Properties feature.

So in order to have it working correctly, you have to add a few changes to your code:

First of all, your CommonDataApplication class should have @EnableConfigurationProperties annotation eg

@EnableAutoConfiguration
@ComponentScan
@PropertySource("application.yml")
@EnableConfigurationProperties
public class CommonDataApplication {

    public static void main(String[] args) {
        // ...
    }
}

I do not believe you need @PropertySource("application.yml") annotation as application.yml (as well as application.properties and application.xml ) is a default configuration file used by Spring Boot.

Your CassandraClientNew class does not need to have @Value annotation prefixing keyspaceApp property. And your keyspaceApp has to have a setter method .

@Component
@ConfigurationProperties(prefix="cassandra")
public class CassandraClientNew {

   private String keyspaceApp;

   public void setKeyspaceApp(final String keyspaceApp) {
       this.keyspaceApp = keyspaceApp;
   }
}

BTW, if you are using List 's or Set s and you initialise collections (eg List<String> values = new ArrayList<>(); ), then only getter is required. If a collection is not initialised then you need to provide a setter method too (otherwise an exception will be thrown).

I hope that will help.

General answer

1. In your properties file (application.properties or application.yml)

# In application.yaml
a:
  b:
    c: some_string

2. Declare your class:

@Component
@ConfigurationProperties(prefix = "a", ignoreUnknownFiels = false)
public class MyClassA {

  public MyClassB theB;   // This name actually does not mean anything
                          // It can be anything      
  public void setTheB(MyClassB theB) {
    this.theB = theB;
  }

  public static MyClassB {

    public String theC;

    public void setTheC(String theC) {
      this.theC = theC;
    }

  }

}

3. Declare public setters! And this is crucial!

Make sure to have these public methods declared in the above classes. Make sure they have modifier.修饰符。

// In MyClassA
public void setTheB(MyClassB theB) {
  this.theB = theB;
}

// In MyClassB
public void setTheC(String theC) {
  this.theC = theC;
}

That's it.

Final notes

  • The property names in your classes do not mean anything to Spring. It only uses public setters. I declared them public not to declare public getters here. Your properties may have any access modifiers.
  • Pay attention to the attribute "ignoreUnknownFields". Its default value is "true". When it is "false" it will throw exception if any of your properties in file "application.yml" was not bound to any class property. It will help you a lot during debugging.

I don't know where the "xyz" came from (maybe you aren't showing your whole application.yml?). You don't normally bind with @Value in @ConfigurationProperties though (it has no way of knowing what your prefix is). Did you actually @EnableCongigurationProperties anywhere? Are you using SpringApplication to create the application context?

只需从 Lombok 添加一个公共setter@Setter

.yml file

cassandra:
    keyspaceApp:junit
solr:
    keyspaceApp:xyz

Bean

@Component
@ConfigurationProperties(prefix="cassandra")
public class CassandraClientNew {
   @Value("${keyspaceApp:@null}") private String keyspaceApp;

Main method file

@EnableAutoConfiguration
@ComponentScan
@PropertySource("application.yml")
public class CommonDataApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(CommonDataApplication.class)
                .web(false).headless(true).main(CommonDataApplication.class).run(args);
    }
}

TestCase

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = CommonDataApplication.class)
@IntegrationTest
@EnableConfigurationProperties
public class CassandraClientTest {

    @Autowired
    CassandraClientNew cassandraClientNew;

    @Test
    public void test(){
        cassandraClientNew.getSession();
        System.out.println(" **** done ****");
    }
}

Instead of setting junit as the keyspaceApp it sets xyz.

Looks like prefix="cassandra" not working

That's my FULL solution for this case:

My Class will be receiving the properties :

// Setter are CRUCIAL for PropertySource + ConfigurationProperties works properly
@PropertySource(value = "classpath:application-dev.yml", ignoreResourceNotFound = true)
@ConfigurationProperties(prefix = "spring.data.mongodb")
@Setter
@Profile("dev")
@Configuration
@EnableReactiveMongoRepositories(
     basePackages = {
          "com.webflux.mongo2.project.repo",
          "com.webflux.mongo2.task.repo"})
public class MyClassGettingTheProperties {
  private String database;
  private String host;
  private String port;
  private String username;
  private String password;
  private String authenticationDatabase;
}

My property file - application-dev.yml :

spring:
  data:
    mongodb:
      database: ${MONGO_INITDB_DATABASE}
      host: ${HOST}
      port: ${PORT}
      username: ${MONGO_INITDB_ROOT_USERNAME}
      password: ${MONGO_INITDB_ROOT_PASSWORD}
      authentication-database: ${AUTH_MAN}

My docker-compose which is the "origin" of the properties:

version: "3.4"

x-common-variables:
  &env-vars
  PORT_API: 1313
  MONGO_INITDB_DATABASE: zzzzz
  HOST: yyyyy
  PORT: 27017
  MONGO_INITDB_ROOT_USERNAME: xxxxx
  MONGO_INITDB_ROOT_PASSWORD: xxxxxxx
  AUTH_MAN: kkkkk

volumes:
  api_vol:

networks:
  mongo-cluster:

services:
  api-db:
    container_name: api-db
    image: mongo:4.4.4
    restart: always
    ports:
      - "27017:27017"
    networks:
      - mongo-cluster
    volumes:
      - api_vol:/data/db
    environment:
      *env-vars

I had a similar but different issue compared to OP, and couldn't find an answer anywhere, so I'll post about it. Let say you're trying to use Lombok to go and auto generate the getters & setters & other fields for you in your configuration file.

Here is an example:

@Configuration
@ConfigurationProperties(prefix = "address")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AddressConfig {
  private Endpoint endpoint;

  @Data
  @NoArgsConstructor
  @AllArgsConstructor
  @Builder
  public static class Endpoint {
    private String url;
  }

}

If using lombok @Getter & @Setter annotations (or @Data which also takes care of @Getter & @Setter ), then make sure to also have this annotation:

@Accessors(fluent = false)

The reason: spring needs the setter methods to actually populate the configuration for you. However, in the above example, here's how the url would be set internally in spring:

addressConfig.getEndpoint().setUrl("...") ;

You can see that getters are prefixed with get and setters are prefixed with set .

If you do NOT have @Accessors(fluent = false) set, then the setters will use the fluent style of accessors, which don't prepend getters with the word get and setters with the word set. This breaks springs ability to populate configuration pojos properly.

Spring will not tell you this when you execute the application. You only get null pointer exceptions when trying when your application tries to use those configuration variables. I only realized I need to put an @Accessors(fluent = false) annotation after hours of trial & error since I couldn't find the answer on google or stackoverflow. side note: intellij will warn you that false is the default value for @Accessors . This is wrong -_-

Anyways, here's the configuration class with the annotation (yes, you only need to add this at the top of the class, just one time, and not in the inner classes).

@Configuration
@ConfigurationProperties(prefix = "address")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AddressConfig {
  private Endpoint endpoint;

  @Data
  @NoArgsConstructor
  @AllArgsConstructor
  @Accessors(fluent = false)
  @Builder
  public static class Endpoint {
    private String url;
  }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM