Injecting List with Spring from yaml

In my recent project, I need to fill a list from configuration file. With my old habits I just tried to use @Value annotation and my code just broke. Here is what I’ve tried first:

application.yaml:

segment:
  list:
    - SEG1
    - SEG2

AppRunner.java:

@Component
public class AppRunner implements ApplicationRunner {

    @Value("${segment.list}")
    private List<String> segmentList;

    @Override
    public void run(ApplicationArguments applicationArguments) throws Exception {
        segmentList.forEach(segment -> System.out.println(segment));
    }

}

However, it just exploded like below:

Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'segment.list' in string value "${segment.list}"
	at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174) ~[spring-core-4.2.4.RELEASE.jar:4.2.4.RELEASE]
	at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126) ~[spring-core-4.2.4.RELEASE.jar:4.2.4.RELEASE]
	at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:204) ~[spring-core-4.2.4.RELEASE.jar:4.2.4.RELEASE]
	at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:178) ~[spring-core-4.2.4.RELEASE.jar:4.2.4.RELEASE]
	at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$2.resolveStringValue(PropertySourcesPlaceholderConfigurer.java:172) ~[spring-context-4.2.4.RELEASE.jar:4.2.4.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:808) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1027) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
	... 23 common frames omitted

After that, I used a workaround for this problem and solve the issue with below annotation style on segmentList and application.yaml:

    @Value("#{'${segment.list}'.split(',')}")
    private List<String> segmentList;

application.yaml:

segment:
  list: SEG1,SEG2

I was basically, splitting a String with comma. It seems fine when I run the application. However, when I run my unit tests I get below error:

Unit test:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = App.class)
public class AppRunnerTest {

    @Autowired
    private AppRunner appRunner;

    @Test
    public void testRun() throws Exception {
        appRunner.run(null);
    }

}

Exception:

Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'segment.list' in string value "#{'${segment.list}'.split(',')}"
 at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
 at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
 at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:204)
 at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:178)
 at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$2.resolveStringValue(PropertySourcesPlaceholderConfigurer.java:172)
 at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:808)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1027)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
 at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545)
 ... 46 more

I’ve looked at a lot of annotation like @TestPropertySource and tried a lot of methods like injecting PropertyPlaceHolder to my test classes to run unit tests with this way but couldn’t manage it. The application context doesn’t aware of my properties because different application context is created when I run tests. At the end, I found this issue on Spring’s Jira system. That was the same issue I had and it guided me to @ConfigurationProperties annotation. And fortunately, I was using Spring Boot and already have this annotation in my path. With this guidance I add a new configuration class, changed application.yaml to list style again and get my list from configurations with the help of @EnableConfigurationProperties annotation at injection:

Config class:

@Configuration
@ConfigurationProperties(prefix = "segment")
public class SegmentListConfig {

    private List<String> list;

    SegmentListConfig() {
        this.list = new ArrayList<>();
    }

    public List<String> getList() {
        return this.list;
    }

}

Runner class:

@Component
@EnableConfigurationProperties
public class AppRunner implements ApplicationRunner {

    @Autowired
    private SegmentListConfig segmentListConfig;

    @Override
    public void run(ApplicationArguments applicationArguments) throws Exception {
        segmentListConfig.getList().forEach(segment -> System.out.println(segment));
    }

}

application.yaml:

segment:
  list:
    - SEG1
    - SEG2

When I dig into a little bit, I found that @ConfigurationProperties can do more. Here is a nice little blog post about how to use it.

I simulated my steps in different commits at this repository. You can check whole working code and not working codes from there.

Developers Rock!!!

Advertisements
This entry was posted in Java and tagged , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s