warrior107
warrior107

Reputation: 759

spring boot read properties from yml file

I have a spring boot application where properties have to be read from a yaml file.

code:

@Component
@PropertySource("classpath:application.yml")
public class ResourceProvider {

    @Autowired
    private Environment env;

    public String getValue(String key) {
        return env.getProperty("app.max.size");
    }
}

yaml file

app:
  max:
    size: 10

When I try this it doesn't work. I get value for app.max.size as null. For size I get the value as 10.

When I use application.properties. I am able to get the desired result. Am I doing anything incorrectly?

application.properties

 app.max.size=10

Upvotes: 7

Views: 65450

Answers (4)

Shubh Shah
Shubh Shah

Reputation: 11

You can't load yaml files using @ProperySource annotation. One thing, I found out was you can use the property spring.config.location where you can define comma separated yaml files location. I have attached the snippet below:

@Data
@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "public.config.app", ignoreUnknownFields = false)
@Slf4j
public class AppServiceConfiguration {

    private Map<String, Object> ios;
    
    private Map<String, Object> android;
    
    private Map<String, Object> buildNumberForIOS;
    
    private Map<String, Object> buildNumberForAndroid;
    
     @PostConstruct
     public void checkIfYamlIsLoaded() {
            log.debug("APP YAML configuration loaded successfully!");
     }
}

This is how i have defined this property in application.properties:

spring.config.location=src/main/resources/app-application.yaml, src/main/resources/web-application.yaml

Here is my sample yaml file:

public:
  config:
    app:
      ios:
        enableMyContent: true
        warnUserOnMultipleShippingMethods: true
        enableShowScanToOrder: false
      android:
        enableMyContent: true
        warnUserOnMultipleShippingMethods: true
        enableShowScanToOrder: false
      buildNumberForIOS:
        '951': enableCurbsidePickup
      buildNumberForAndroid:
        '585': enableCurbsidePickup
        '595': enableImpulseUpsellOnPDP|enableQuantityLimiter

Upvotes: 1

Toerktumlare
Toerktumlare

Reputation: 14819

from the documentation:

YAML files cannot be loaded by using the @PropertySource annotation. So, in the case that you need to load values that way, you need to use a properties file.

Official spring boot documentation reference

Upvotes: 1

Sachith Dickwella
Sachith Dickwella

Reputation: 1490

Since you'r using application.yml file, you don't need to manually load the file to the context as it's the default config file for spring application. You can simply use them in a @Component decorated class like below;

@Value("${app.max.size}")
private int size;

If you're laoding custom YAML file, then this is a huge problem in Spring yet. Using @PropertySource you can't load YAML files simply. It's possible, but little work required. First you need a custom property source factory. In your case, custom YAML property source factory.

import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Objects;
import java.util.Properties;

public class YamlPropertySourceFactory implements PropertySourceFactory {

    /**
     * Create a {@link PropertySource} that wraps the given resource.
     *
     * @param name     the name of the property source
     * @param resource the resource (potentially encoded) to wrap
     * @return the new {@link PropertySource} (never {@code null})
     * @throws IOException if resource resolution failed
     */
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource)
            throws IOException {
        Properties properties = load(resource);
        return new PropertiesPropertySource(name != null ? name :
                Objects.requireNonNull(resource.getResource().getFilename(), "Some error message"),
                properties);
    }

    /**
     * Load properties from the YAML file.
     *
     * @param resource Instance of {@link EncodedResource}
     * @return instance of properties
     */
    private Properties load(EncodedResource resource) throws FileNotFoundException {
        try {
            YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
            factory.setResources(resource.getResource());
            factory.afterPropertiesSet();

            return factory.getObject();
        } catch (IllegalStateException ex) {
            /*
             * Ignore resource not found.
             */
            Throwable cause = ex.getCause();
            if (cause instanceof FileNotFoundException) throw (FileNotFoundException) cause;
            throw ex;
        }
    }
}

And, you need to tell @PropertySource annotation use this factory instead of default one whenever you use it like below;

@Component
@PropertySource(value = "classpath:config-prop.yml", factory = YamlPropertySourceFactory.class) // Note the file name with the extension unlike a property file. Also, it's not the `application.yml` file.
public class ResourceProvider { 

    @Value("${app.max.size}")
    private int size;
}

You can use your properties shown in above code snippet's size variable.

Though. If you're using a YAML array declaration to get properties, that would be little peculiar even if you use this way.

Upvotes: 12

Arun Girivasan
Arun Girivasan

Reputation: 602

You can read the value by:

@Value("${app.max.size}")
private String size;  

public String getValue(String key) {
   return size;
}

Upvotes: -2

Related Questions