45

I have a spring application that is currently using *.properties files and I want to have it using YAML files instead.

I found the class YamlPropertiesFactoryBean that seems to be capable of doing what I need.

My problem is that I'm not sure how to use this class in my Spring application (which is using annotation based configuration). It seems I should configure it in the PropertySourcesPlaceholderConfigurer with the setBeanFactory method.

Previously I was loading property files using @PropertySource as follows:

@Configuration
@PropertySource("classpath:/default.properties")
public class PropertiesConfig {

    @Bean
    public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
}

How can I enable the YamlPropertiesFactoryBean in the PropertySourcesPlaceholderConfigurer so that I can load YAML files directly? Or is there another way of doing this?

Thanks.

My application is using annotation based config and I'm using Spring Framework 4.1.4. I found some information but it always pointed me to Spring Boot, like this one.

ktulinho
  • 3,870
  • 9
  • 28
  • 35

5 Answers5

80

With XML config I've been using this construct:

<context:annotation-config/>

<bean id="yamlProperties" class="org.springframework.beans.factory.config.YamlPropertiesFactoryBean">
    <property name="resources" value="classpath:test.yml"/>
</bean>

<context:property-placeholder properties-ref="yamlProperties"/>

Of course you have to have the snakeyaml dependency on your runtime classpath.

I prefer XML config over the java config, but I recon it shouldn't be hard to convert it.

edit:
java config for completeness sake

@Bean
public static PropertySourcesPlaceholderConfigurer properties() {
  PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
  YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
  yaml.setResources(new ClassPathResource("default.yml"));
  propertySourcesPlaceholderConfigurer.setProperties(yaml.getObject());
  return propertySourcesPlaceholderConfigurer;
}
  • 5
    Thanks for your answer. How can I now inject a value from the `default.yml` into my Java class? So if my yml contains a property `value: 60`, can I simple do this in my code: `@Value("${value}") private String value;`? – ktulinho Aug 11 '15 at 09:35
  • 3
    You can use the properties loaded from the yaml file as you would use the regular .properties file. So in any Spring managed class you can indeed use your exact example of `@Value("${value}") private String value`, and it'll be injected. – turtlesallthewaydown Aug 11 '15 at 13:10
  • 2
    how to load multiple files? – spandey Mar 22 '18 at 08:23
  • 1
    [setResources()](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/YamlProcessor.html#setResources-org.springframework.core.io.Resource...-) takes a varargs argument, so you can add multiple in the same call. Don't call the method multiple times with different arguments, since it [overwrites](https://github.com/spring-projects/spring-framework/blob/master/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java#L121) the resources on each call. – turtlesallthewaydown Mar 26 '18 at 06:48
  • Is there a way to inject these properties into a tag? Currently I can only find examples with location. EG: – TheJeff Apr 09 '18 at 22:47
  • 1
    @TheJeff the [xsd schema](http://www.springframework.org/schema/util/spring-util.xsd) only allows for a location attribute, which states: `The location of the properties file, as a Spring resource location: a URL, a "classpath:" pseudo URL, or a relative file path. Multiple locations may be specified, separated by commas.`. You can probably use the [non shorthand notation](https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#xsd-schemas-util-properties) to customise the configuration. – turtlesallthewaydown Jul 18 '18 at 07:09
  • How can I map yaml block to Map attribute via @Value annotation ? – Simon Logic Feb 26 '19 at 16:35
  • 1
    what should i give the path if the file is located externally outside src/main/resources , say in external config folder? – prnjn Oct 05 '19 at 15:43
  • 1
    @prnjn You can use any of the following, depending on your preferred method: [PathResource](https://docs.spring.io/spring/docs/4.3.25.RELEASE/javadoc-api/org/springframework/core/io/PathResource.html), [FileSystemResource](https://docs.spring.io/spring/docs/4.3.25.RELEASE/javadoc-api/org/springframework/core/io/FileSystemResource.html), or [InputStreamResource](https://docs.spring.io/spring/docs/4.3.25.RELEASE/javadoc-api/org/springframework/core/io/InputStreamResource.html). – turtlesallthewaydown Oct 07 '19 at 07:00
  • @turtlesallthewaydown i used FileSystemResource and it worked, But now i am facing issue that it is working fine in eclipse , but in intelijIdea i am getting exception java.io.FileNotFoundException: ./conf/myFile.yml – prnjn Oct 08 '19 at 05:13
5

To read .yml file in Spring you can use next approach.

For example you have this .yml file:

section1:
  key1: "value1"
  key2: "value2"
section2:
  key1: "value1"
  key2: "value2"

Then define 2 Java POJOs:

@Configuration
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "section1")
public class MyCustomSection1 {
    private String key1;
    private String key2;

    // define setters and getters.
}

@Configuration
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "section2")
public class MyCustomSection1 {
    private String key1;
    private String key2;

    // define setters and getters.
}

Now you can autowire these beans in your component. For example:

@Component
public class MyPropertiesAggregator {

    @Autowired
    private MyCustomSection1 section;
}

In case you are using Spring Boot everything will be auto scaned and instantiated:

@SpringBootApplication
public class MainBootApplication {
     public static void main(String[] args) {
        new SpringApplicationBuilder()
            .sources(MainBootApplication.class)
            .bannerMode(OFF)
            .run(args);
     }
}

If you'are using JUnit there is a basic test setup for loading YAML file:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(MainBootApplication.class)
public class MyJUnitTests {
    ...
}

If you're using TestNG there is a sample of test configuration:

@SpringApplicationConfiguration(MainBootApplication.class)
public abstract class BaseITTest extends AbstractTestNGSpringContextTests {
    ....
}
Macskasztorik
  • 649
  • 2
  • 7
  • 17
ayurchuk
  • 1,879
  • 1
  • 17
  • 13
4

`

package com.yaml.yamlsample;

import com.yaml.yamlsample.config.factory.YamlPropertySourceFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;

@SpringBootApplication
@PropertySource(value = "classpath:My-Yaml-Example-File.yml", factory = YamlPropertySourceFactory.class)
public class YamlSampleApplication implements CommandLineRunner {

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

    @Value("${person.firstName}")
    private String firstName;
    @Override
    public void run(String... args) throws Exception {
        System.out.println("first Name              :" + firstName);
    }
}


package com.yaml.yamlsample.config.factory;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.DefaultPropertySourceFactory;
import org.springframework.core.io.support.EncodedResource;
import java.io.IOException;
import java.util.List;

public class YamlPropertySourceFactory extends DefaultPropertySourceFactory {
    @Override
    public PropertySource createPropertySource(String name, EncodedResource resource) throws IOException {
         if (resource == null) {
            return super.createPropertySource(name, resource);
        }
        List<PropertySource<?>> propertySourceList = new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource());
        if (!propertySourceList.isEmpty()) {
            return propertySourceList.iterator().next();
        }
        return super.createPropertySource(name, resource);
    }
}

My-Yaml-Example-File.yml

person:
  firstName: Mahmoud
  middleName:Ahmed

Reference my example on github spring-boot-yaml-sample So you can load yaml files and inject values using @Value()

1

I spend 5 to 6 hours in understanding why external configuration of yml/yaml file(not application.yml) are so different.I read various articles, stack overflow questions but didn't get correct answer.

I was stuck in between like I was able to use custom yml file value using YamlPropertySourceLoader but not able to use @Value because it is giving me error like Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'fullname.firstname' in value "${fullname.firstname}"

fullname is a property in yml.

Then I used "turtlesallthewaydown" given above solution, then at last I was able to use @Value without any issues for yaml files and I removed YamlPropertySourceLoader.

Now I understand the difference between YamlPropertySourceLoader and PropertySourcesPlaceholderConfigurer, a big thanks, however I have added these changes in my git repo.

Git Repo: https://github.com/Atishay007/spring-boot-with-restful-web-services

Class Name: SpringMicroservicesApplication.java

Atishay Jain
  • 87
  • 1
  • 9
0

If one will seek how to load yaml file into Properties in Spring, then there is a solution:

public Properties loadYaml(String fileName){
  // fileName for eg is "my-settings.yaml"
  YamlPropertySourceLoader ypsl = new YamlPropertySourceLoader();
  PropertySource ps = ypsl.load(fileName, new ClassPathResource(fileName)).get(0);
  Properties props = new Properties();
  props.putAll((Map)ps.getSource());
  return props;
}
Yan Pak
  • 1,767
  • 2
  • 19
  • 15