Search code examples
springspring-bootproperty-binding

@ConfigurationProperties Set<Type>


I'd like to have spring load some properties on startup and convert them into the proper types.

application.yml:

base:
  security:

    scopes:
      - name: read
        descripton: read some nice things

      - name: write
        description: write some nice things

    authorities:
      - name: ROLE_ADMIN
        scopes:
          - read
          - write

      - name: ROLE_USER
        scopes:
          - read 

To load these properties into types I used the following @ConfigurationProperties: BaseProperties.java

@Setter
@Getter
@Configuration
@ConfigurationProperties(prefix="base.security")
public class BaseProperties {

    private Set<ScopeProperty> scopes = new HashSet<>();
    private Set<AuthorityProperty> authorities = new HashSet<>();

    @Getter
    @Setter
    public class AuthorityProperty {
        private String name;
        private List<String> scopes;
    }

    @Getter
    @Setter
    public class ScopeProperty {
        private String name;
        private String description;
    }
}

Everything I get is a BindException as follows:

Caused by: org.springframework.boot.context.properties.bind.UnboundConfigurationPropertiesException: The elements [base.security.authorities[0].name,base.security.authorities[0].scopes[0],base.security.authorities[0].scopes[1],base.security.authorities[1].name,base.security.authorities[1].scopes[0]] were left unbound.
    at org.springframework.boot.context.properties.bind.IndexedElementsBinder.assertNoUnboundChildren(IndexedElementsBinder.java:136)
    at org.springframework.boot.context.properties.bind.IndexedElementsBinder.bindIndexed(IndexedElementsBinder.java:113)
    at org.springframework.boot.context.properties.bind.IndexedElementsBinder.bindIndexed(IndexedElementsBinder.java:86)
    at org.springframework.boot.context.properties.bind.IndexedElementsBinder.bindIndexed(IndexedElementsBinder.java:71)
    at org.springframework.boot.context.properties.bind.CollectionBinder.bindAggregate(CollectionBinder.java:49)
    at org.springframework.boot.context.properties.bind.AggregateBinder.bind(AggregateBinder.java:56)
    at org.springframework.boot.context.properties.bind.Binder.lambda$bindAggregate$2(Binder.java:293)
    at org.springframework.boot.context.properties.bind.Binder$Context.withIncreasedDepth(Binder.java:429)
    at org.springframework.boot.context.properties.bind.Binder$Context.access$100(Binder.java:372)
    at org.springframework.boot.context.properties.bind.Binder.bindAggregate(Binder.java:293)
    at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:254)
    at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:214)

solution:

Either mark the classes as static or create them as seperate classes:

    @Getter
    @Setter
    public static class AuthorityProperty {
        private String name;
        private List<String> scopes;
    }

    @Getter
    @Setter
    public static class ScopeProperty {
        private String name;
        private String description;
    }

Solution

  • Your classes are inner classes which require an instance of outer class BaseProperties to be constructed, which is not supported by Spring Properties.

    You can mark your classes with keyword static which is supposed to work

    @Getter
    @Setter
    public static class AuthorityProperty {
        private String name;
        private List<String> scopes;
    }