How to use YamlPropertiesFactoryBean to load YAML files using Spring Framework 4.1?

84,274

Solution 1

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;
}

Solution 2

`

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()

Solution 3

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 {
    ....
}

Solution 4

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

Solution 5

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;
}
Share:
84,274
ktulinho
Author by

ktulinho

Software Engineer with 12 years of experience developing, deploying and maintaining client and server applications. Ability to design and develop applications using Object Oriented concepts, Software Engineering concepts and methodologies such as Scrum and Agile. Specialties: Java EE, Java SE, Maven, Git, SQL, IntelliJ, Databases and a bit of DevOps. Continuously improving skills and knowledge through courses, articles and technical conferences. Interested by: Serverless, Cloud and Server-Side Applications; Desktop and mobile application design, development, usability and improving user experience; New technologies such as IoT, Machine Learning, VR and video games!

Updated on July 09, 2022

Comments

  • ktulinho
    ktulinho almost 2 years

    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.

    • turtlesallthewaydown
      turtlesallthewaydown almost 9 years
      If my comment answers your initial question, could you accept it? Or if there is anything else I can add, let me now.
    • ktulinho
      ktulinho almost 9 years
      @turtlesallthewaydown Thanks for your answer and sorry for the delay in the response.
  • ktulinho
    ktulinho almost 9 years
    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;?
  • turtlesallthewaydown
    turtlesallthewaydown almost 9 years
    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.
  • Timur Milovanov
    Timur Milovanov almost 8 years
    Sorry, but your example relates to Spring Boot. The question is about plain Spring Framework 4.1
  • niaomingjian
    niaomingjian about 7 years
    @ayurchuk my file name is 'myconfig.yaml'. This method doesn't work. What should I do?
  • spandey
    spandey about 6 years
    how to load multiple files?
  • turtlesallthewaydown
    turtlesallthewaydown about 6 years
    setResources() 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 the resources on each call.
  • Ahmed Kooli
    Ahmed Kooli about 6 years
    Where are you putting your yml file? How spring boot is loading it ? You should mention it some where ?
  • TheJeff
    TheJeff about 6 years
    Is there a way to inject these properties into a <util:properties> tag? Currently I can only find examples with location. EG: <util:properties id="env" location="classpath:env_dev.properties"/>
  • turtlesallthewaydown
    turtlesallthewaydown almost 6 years
    @TheJeff the xsd schema 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 to customise the configuration.
  • Simon Logic
    Simon Logic about 5 years
    How can I map yaml block to Map attribute via @Value annotation ?
  • prnjn
    prnjn over 4 years
    what should i give the path if the file is located externally outside src/main/resources , say in external config folder?
  • turtlesallthewaydown
    turtlesallthewaydown over 4 years
    @prnjn You can use any of the following, depending on your preferred method: PathResource, FileSystemResource, or InputStreamResource.
  • prnjn
    prnjn over 4 years
    @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
  • gorjanz
    gorjanz about 4 years
    The question is regarding core spring application, and this answer is relying on spring boot being used, hence the down-vote.