Spring configuration properties recursive type

Viewed 2003

I am trying to create a configuration properties class that has a recursive class, structured similarly to a linked list. I'm using Spring boot 2.0.6.RELEASE, and the class is being autowired using @EnableConfigurationProperties({EnginealConfig.class}).

The issue I am having is that only one the first level will be bound to the Test object, x.test will never get set.

Using the following application.properties file:

engineal.x.value: "Test1"
engineal.x.test.value: "Test2"
engineal.x.test.test.value: "Test3"

And the following configuration properties class:

@ConfigurationProperties(prefix = "engineal")
public class EnginealConfig {

    static class Test {

        private String value;
        private Test test;

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }

        public Test getTest() {
            return test;
        }

        public void setTest(Test test) {
            this.test = test;
        }

        @Override
        public String toString() {
            return "Test{" +
                    "value='" + value + '\'' +
                    ", test=" + test +
                    '}';
        }
    }

    private Test x;

    public Test getX() {
        return x;
    }

    public void setX(Test x) {
        this.x = x;
    }

    @Override
    public String toString() {
        return "EnginealConfig{" +
                "x=" + x +
                '}';
    }
}

the object will print EnginealConfig{x=Test{value='Test1', test=null}}. Unfortunately the recursion is not working.

After messing around trying different things to get this to work, I tried changing EnginealConfig#Test.test from private Test test; to private List<Test> test;, along with the getters and setters. Then by using lists with one element, this recursion works.

The following application.properties with the List<Test> change:

engineal.x.value: "Test1"
engineal.x.test[0].value: "Test2"
engineal.x.test[0].test[0].value: "Test3"

will output EnginealConfig{x=Test{value='Test1', test=[Test{value='Test2', test=[Test{value='Test3', test=null}]}]}}. I can then access the next element by using test.get(0).

So it appears as if recursion is supported only if the recursive type is in a collection.

While this workaround is ok, I would prefer to use my first way of doing it. Are/should recursive classes be supported without needing a collection? Thank you for your help!

2 Answers

Just set your inner class as an individual class, then everything would work well.

application.yml

simple:
  value: aa
  myTest:
    value: lower

config class

@ConfigurationProperties(prefix = "simple")
@Data //It is for getter setter
public class SimpleConfig {

    private String value;

    private MyTest myTest;
}

the recursion class

@Data
public class MyTest {
    private String value;
    private MyTest myTest;
}

Test case

@Resource
private SimpleConfig simpleConfig;

@Test
public void myTest(){

    String value = simpleConfig.getValue();
    System.out.println("outerValue : " + value);
    String innerValue = simpleConfig.getMyTest().getValue();
    System.out.println("innerValue :" + innerValue);
}

result

outerValue : aa
innerValue :lower
@ConfigurationProperties(prefix = "engineal")
public class EnginealConfig {

    static class Test {
        //@NestedConfigurationProperty
        private String value;

        @NestedConfigurationProperty
        private Test test;

You may annotate the field with @NestedConfigurationProperty annotation.

Related