javaspringspring-mvcspring-bootproperty-placeholder

SpringBoot don't replacen System variable {user.home} in Spring Tool Suite Version: 3.8.4.RELEASE


I've generated a Spring Boot web application using Spring Initializr, using embedded Tomcat + Thymeleaf template engine in a macOS Sierra. I want to use the System variable User Home Folder Name in Mac OS

I have this Spring class configuration in my Spring Boot application

@Configuration
@Profile("dev")
@PropertySource("file:///{user.home}/.devopsbuddy/application-dev.properties")
public class DevelopmentConfig {

    @Bean
    public EmailService emailService() {
        return new MockEmailService();
    }

}

But I got this error when I start the application

Caused by: java.io.FileNotFoundException: /{user.home}/.devopsbuddy/application-dev.properties (No such file or directory)
    at java.io.FileInputStream.open0(Native Method)
    at java.io.FileInputStream.open(FileInputStream.java:195)
    at java.io.FileInputStream.<init>(FileInputStream.java:138)
    at java.io.FileInputStream.<init>(FileInputStream.java:93)
    at sun.net.www.protocol.file.FileURLConnection.connect(FileURLConnection.java:90)
    at sun.net.www.protocol.file.FileURLConnection.getInputStream(FileURLConnection.java:188)
    at org.springframework.core.io.UrlResource.getInputStream(UrlResource.java:169)
    at org.springframework.core.io.support.EncodedResource.getInputStream(EncodedResource.java:154)
    at org.springframework.core.io.support.PropertiesLoaderUtils.fillProperties(PropertiesLoaderUtils.java:98)
    at org.springframework.core.io.support.PropertiesLoaderUtils.fillProperties(PropertiesLoaderUtils.java:72)
    at org.springframework.core.io.support.PropertiesLoaderUtils.loadProperties(PropertiesLoaderUtils.java:58)
    at org.springframework.core.io.support.ResourcePropertySource.<init>(ResourcePropertySource.java:65)
    at org.springframework.core.io.support.DefaultPropertySourceFactory.createPropertySource(DefaultPropertySourceFactory.java:36)
    at org.springframework.context.annotation.ConfigurationClassParser.processPropertySource(ConfigurationClassParser.java:440)
    at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:271)
    at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:245)
    at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:190)
    at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:292)
    at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:245)
    at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:198)
    at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:167)
    ... 13 common frames omitted

Solution

  • @PropertySource was first added as part of Spring 3.1 for importing resources

    In the @PropertySource Java doc it is stated that,

    Any ${…} placeholders present in a @PropertySource resource location will be resolved against the set of property sources already registered against the environment.

    For your issue, you have missed to add the dollar sign($). Hopefully after adding dollar sign, your problem will be solved.

    You can add @PropertySource in many ways. For basic use,

    @Configuration
    @PropertySource(value = "classpath:application.properties")
    public class ApplicationConfig {
    
        // more configuration ...
    }
    

    When executed, properties will be imported from the application.properties file, located in the classpath root. classpath is the default location, and can thus be omitted:

    @Configuration
    @PropertySource("application.properties")
    public class ApplicationConfig {
    }
    

    Alternatively, it is possible to specify a file: location to appoint a properties file that is located elsewhere on your host environment:

    @PropertySource("file:/path/to/application.properties")
    

    or you can use

    @PropertySource("file:${CONF_DIR}/application.properties")
    
    $ echo $CONF_DIR
    /path/to/directory/with/app/config/files
    

    In your case, you have to use `

    @PropertySource("file:${user.home}/application.properties")

    `

    and $user.home will give the following location. You will put your properties file on that location.

     $ echo $user.home
     /home/yourusername
    

    In Spring 4:

    Spring 4 brings two new features to the @ProperySource.

    First new feature:

    It deals with missing files. By default, Spring will throw an exception if it does not find the file that has been declared.

    @PropertySource(value = "missing.properties", ignoreResourceNotFound = true)
    

    If you don't use ignoreResourceNotFound = true, it will give the following error.

    java.lang.IllegalStateException: Failed to load ApplicationContext
    [...]
    Caused by: java.io.FileNotFoundException: class path resource [missing.properties] cannot be opened because it does not exist.
    

    Second new feature:

    Secondly, there is a new annotation called @PropertySources that allows you to declare repeated @PropertySource annotations:

    @PropertySources({
        @PropertySource("default.properties"),
        @PropertySource("overriding.properties")
    })
    

    N.B: Yet again, the order of the property file declarations is important. As suggested by the file names in the example above, files declared later will override any previous value(s) if they contain the same key(s).

    Putting It Together

    To summarize, a property source configuration can be implemented like:

    @Configuration
    @PropertySources({
        @PropertySource("default.properties"),
        @PropertySource(value = "file:${CONF_DIR}/optional-override.properties", ignoreResourceNotFound = true)
    }
    public class ApplicationConfig {
    }
    

    In Java 8


    In Java 8, the @PropertySources annotation will be redundant, because Java8 introduces the repeating annotation. This means same annotations can be repeated as much as you want at same locations.

    Prior to Java8, to have a repeated annotation, will have to group them in a container annotation

    @Manufactures({
    @Manufacturer(name =”BMW”),
    @Manufacturer(name = “Range Rover”)
    
    })
    public class Car{
    //code goes in here
    }
    

    With Java8 repeating annotations, it gives us the flexibility to write the same thing without any container annotation

    @Manufacturer(name = “BMW”)
    @Manufacturer(name= “Range Rover”)
    public class Car{
    //code goes in here
    }
    

    Though the container annotation was not used here, the Java compiler this time around takes responsibility for wrapping the two annotations into a container.

    All credit goes to Mattias Severson

    Resource Link:

    1. Spring @PropertySource
    2. Java 8 Repeating Annotation Explained in 5 minutes