66

Is it possible to have multiple application.properties file? (EDIT: note that this question evolved to the one on the title.)

I tried to have 2 files.

  • The first one is on the root folder in the application Jar.
  • The second one is on the directory which is specified in classpath.

2 files are both named 'application.properties'.

Is it possible to 'merge' the contents of both files? (and the second one's property values override the first one's) Or, if I have one file then the other file is ignored?

UPDATE 1: it is possible to 'merge' the contents. Yesterday it seemed that the first one was ignored, but it seems that it's because that something was broken then. Now it works well.

UPDATE 2: It's back again! Again only one of two files is being applied. It's strange... It started after I built the app jar file using Spring Tool Suite. And it seems that the Jar version always ignores the second one (in classpath), while the behavior of the expanded version which runs on STS varies. From where can I start to investigate?

UPDATE 3:

The behavior of the Jar version was in fact correct. It's the specification of java.exe. When -jar option is specified, java.exe ignores both -classpath option and CLASSPATH environment variable, and the classpath will contain only the jar file. So, the second application.properties file on the classpath is ignored.

Now, how can I let the second application.properties on the classpath be loaded?

UPDATE 4:

I managed to load an application.properties file in external path while using -jar option.

The key was PropertiesLauncher.

To use PropertiesLauncher, pom.xml file must be changed like this:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>  <!-- added -->
                <layout>ZIP</layout> <!-- to use PropertiesLaunchar -->
            </configuration>
        </plugin>
    </plugins>
</build>

For this, I referenced the following StackOverflow question: spring boot properties launcher unable to use . BTW, In Spring Boot Maven Plugin document(http://docs.spring.io/spring-boot/docs/1.1.7.RELEASE/maven-plugin/repackage-mojo.html), there is no mention that specifying ZIP triggers that PropertiesLauncher is used. (Perhaps in another document?)

After the jar file had been built, I could see that the PropertiesLauncher is used by inspecting Main-Class property in META-INF/MENIFEST.MF in the jar.

Now, I can run the jar as follows(in Windows):

java -Dloader.path=file:///C:/My/External/Dir,MyApp-0.0.1-SNAPSHOT.jar -jar MyApp-0.0.1-SNAPSHOT.jar

Note that the application jar file is included in loader.path.

Now an application.properties file in C:\My\External\Dir\config is loaded.

As a bonus, any file (for example, static html file) in that directory can also be accessed by the jar since it's in the loader path.

As for the non-jar (expanded) version mentioned in UPDATE 2, maybe there was a classpath order problem.

(BTW, I changed the question's title to be more specific to this issue.)

7
  • Can you please explain how you are building and running the jar? Thanks
    – geoand
    Commented Oct 7, 2014 at 7:59
  • @geoand The jar is built using Spring Tool Suite. It actually executes 'mvn package'. It builds a fat jar. In MANIFEST.MF file in the Jar, Main-Class is org.springframework.boot.loader.JarLauncher. MANIFEST.MF is created by the spring boot maven plugin.
    – zeodtr
    Commented Oct 7, 2014 at 8:14
  • @geoand I used to run the Jar with 'java -cp D:\My\External\Dir -jar MyApp-0.0.1-SNAPSHOT.jar', but now I know the -cp is ignored by java.
    – zeodtr
    Commented Oct 7, 2014 at 8:16
  • @geoand I managed to load an external application.properties file. Please see my UPDATE 4.
    – zeodtr
    Commented Oct 7, 2014 at 9:07
  • 1
    Thanks a lot. Am I the only one to feel that it's harder than it should be, given that everything else is straightforward ? Commented Nov 18, 2014 at 18:30

12 Answers 12

54

If you have not changed the defaults of Spring Boot (meaning you are using @EnableAutoConfiguration or @SpringBootApplication and have not changed any Property Source handling), then it will look for properties with the following order (highest overrides lowest):

  1. A /config subdir of the current directory
  2. The current directory
  3. A classpath /config package
  4. The classpath root

The list above is mentioned in this part of the documentation

What that means is that if a property is found for example application.properties under src/resources is will be overridden by a property with the same name found in application.properties in the /config directory that is "next" to the packaged jar.

This default order used by Spring Boot allows for very easy configuration externalization which in turn makes applications easy to configure in multiple environments (dev, staging, production, cloud etc)

To see the whole set of features provided by Spring Boot for property reading (hint: there is a lot more available than reading from application.properties) check out this part of the documentation.

As one can see from my short description above or from the full documentation, Spring Boot apps are very DevOps friendly!

7
  • 2
    Spring Boot definitely merges the contents of each property file using the priorities mentioned above. Glad it worked for you!!
    – geoand
    Commented Oct 2, 2014 at 4:48
  • It's back again! Again only one of two files is being applied. See my UPDATE 2 on the question. And since it seems that the root problem is not solved, I temporarily undid the accept the answer for now. Sorry.
    – zeodtr
    Commented Oct 2, 2014 at 11:47
  • @zeodtr Don't worry about unaccepting! Maybe it's a problem with the build. Have you tried doing a Maven clean and building the boot jar from Maven?
    – geoand
    Commented Oct 2, 2014 at 13:35
  • Yes, in fact the STS project is based on maven. I've did maven clean and package (which is run by STS). Maybe I should try directly on the command line. BTW, the OS of my machine is Windows 7.
    – zeodtr
    Commented Oct 2, 2014 at 17:29
  • I don't use STS so I don't really have any more info on that. However I haven't been able to reproduce your problem
    – geoand
    Commented Oct 3, 2014 at 8:48
19

It's all explained here in the docs:

http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html

Which explains that this is the order of precedence:

  • A /config subdir of the current directory.
  • The current directory
  • A classpath /config package
  • The classpath root

It also points out that you can define additional properties files for overrides like so:

java -jar myproject.jar 
    --spring.config.location=classpath:/overrides.properties

If you use spring.config.location, then all the default locations for application.properties are also included. This means that you can set up default values in application.properties and override as required for a particular environment.

2
  • 1
    Thanks. But I've read the doc. It is unclear that whether the overriding is value-by-value basis (so that you can merge the values in multiple files) or file basis (in this case you can only choose one file - the other files are ignored). My experience tells that it's the latter case. Maybe I missed something.
    – zeodtr
    Commented Oct 2, 2014 at 0:10
  • Now it works well. it is possible to 'merge' the contents. Yesterday it seemed that the first file was ignored, but it seems that it's because that something was broken then. Thanks.
    – zeodtr
    Commented Oct 2, 2014 at 1:19
12

I managed to load an application.properties file in external path while using -jar option.

The key was PropertiesLauncher.

To use PropertiesLauncher, pom.xml file must be changed like this:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>  <!-- added -->
                <layout>ZIP</layout> <!-- to use PropertiesLaunchar -->
            </configuration>
        </plugin>
    </plugins>
</build>

For this, I referenced the following StackOverflow question: spring boot properties launcher unable to use . BTW, In Spring Boot Maven Plugin document(http://docs.spring.io/spring-boot/docs/1.1.7.RELEASE/maven-plugin/repackage-mojo.html), there is no mention that specifying ZIP triggers that PropertiesLauncher is used. (Perhaps in another document?)

After the jar file had been built, I could see that the PropertiesLauncher is used by inspecting Main-Class property in META-INF/MENIFEST.MF in the jar.

Now, I can run the jar as follows(in Windows):

java -Dloader.path=file:///C:/My/External/Dir,MyApp-0.0.1-SNAPSHOT.jar -jar MyApp-0.0.1-SNAPSHOT.jar

Note that the application jar file is included in loader.path.

Now an application.properties file in C:\My\External\Dir\config is loaded.

As a bonus, any file (for example, static html file) in that directory can also be accessed by the jar since it's in the loader path.

As for the non-jar (expanded) version mentioned in UPDATE 2, maybe there was a classpath order problem.

3
  • Due to some reason, this is not working for me. I am using spring boot 2.1.1. I tried setting layout but this doesn't change anything for me. Just a background: I have ClassLoader.getSystemResource(path) in one of my dependencies and due to this I want to make config available using -Dloader.path, but it is not picking even after performing the mentioned steps. @zeodtr can you point what might be getting wrong here?
    – tom
    Commented Feb 25, 2019 at 10:32
  • @tom Sorry, I'm afraid I don't know. In Spring Boot 1.x it worked. And it's working after 2.x upgrade. But now I don't maintain the related source code and almost forgot Spring Boot details. Sorry again.
    – zeodtr
    Commented Feb 25, 2019 at 13:24
  • no worries! I'll try to post a new question then. Thanks!
    – tom
    Commented Feb 26, 2019 at 3:01
9

You would be able to launch your spring boot appication with the external properties file path as follows:

java -jar {jar-file-name}.jar 
--spring.config.location=file:///C:/{file-path}/{file-name}.properties
2
  • Thanks, if one does not need the 'bonus' I've mentioned my UPDATE 4, this solution is simpler.
    – zeodtr
    Commented Jul 21, 2015 at 5:20
  • I have tried with your UPDATE 4 (mentioned below) and it did not work. So with the above command I was able to fetch properties from external file path. java -Dloader.path=file:///C:/My/External/Dir,MyApp-0.0.1-SNAPSHOT.jar -jar MyApp-0.0.1-SNAPSHOT.jar This solution worked for me hence I posted it and not to earn any kind of 'bonus'
    – kk.
    Commented Jul 22, 2015 at 9:05
2
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <layout>ZIP</layout> 
            </configuration>
        </plugin>
    </plugins>
</build>

java -Dloader.path=file:///absolute_path/external.jar -jar example.jar
0
2
java -jar server-0.0.1-SNAPSHOT.jar --spring.config.location=application-prod.properties
1

I know it is a pointed question, and the op wanted to load different properties file.

My answer is, doing custom hacks like this is a terrible idea.

If you are using spring-boot with a cloud provider such as cloud foundry, please do yourself a favor and use cloud config services

https://spring.io/projects/spring-cloud-config

It loads and merges default/dev/project-default/project-dev specific properties like magic

Again, Spring boot already gives you enough ways to do this right https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html

Please do not re-invent the wheel.

0

This may be coming in Late but I think I figured out a better way to load external configurations especially when you run your spring-boot app using java jar myapp.war instead of @PropertySource("classpath:some.properties")

The configuration would be loaded form the root of the project or from the location the war/jar file is being run from

public class Application implements EnvironmentAware {

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

    @Override
    public void setEnvironment(Environment environment) {
        //Set up Relative path of Configuration directory/folder, should be at the root of the project or the same folder where the jar/war is placed or being run from
        String configFolder = "config";
        //All static property file names here
        List<String> propertyFiles = Arrays.asList("application.properties","server.properties");
        //This is also useful for appending the profile names
        Arrays.asList(environment.getActiveProfiles()).stream().forEach(environmentName -> propertyFiles.add(String.format("application-%s.properties", environmentName))); 
        for (String configFileName : propertyFiles) {
            File configFile = new File(configFolder, configFileName);
            LOGGER.info("\n\n\n\n");
            LOGGER.info(String.format("looking for configuration %s from %s", configFileName, configFolder));
            FileSystemResource springResource = new FileSystemResource(configFile);
            LOGGER.log(Level.INFO, "Config file : {0}", (configFile.exists() ? "FOund" : "Not Found"));
            if (configFile.exists()) {
                try {
                    LOGGER.info(String.format("Loading configuration file %s", configFileName));
                    PropertiesFactoryBean pfb = new PropertiesFactoryBean();
                    pfb.setFileEncoding("UTF-8");
                    pfb.setLocation(springResource);
                    pfb.afterPropertiesSet();
                    Properties properties = pfb.getObject();
                    PropertiesPropertySource externalConfig = new PropertiesPropertySource("externalConfig", properties);
                    ((ConfigurableEnvironment) environment).getPropertySources().addFirst(externalConfig);
                } catch (IOException ex) {
                    LOGGER.log(Level.SEVERE, null, ex);
                }
            } else {
                LOGGER.info(String.format("Cannot find Configuration file %s... \n\n\n\n", configFileName));

            }

        }
    }

}

Hope it helps.

0

Another flexible way using classpath containing fat jar (-cp fat.jar) or all jars (-cp "$JARS_DIR/*") and another custom config classpath or folder containing configuration files usually elsewhere and outside jar. So instead of the limited java -jar, use the more flexible classpath way as follows:

java \
   -cp fat_app.jar \ 
   -Dloader.path=<path_to_your_additional_jars or config folder> \
   org.springframework.boot.loader.PropertiesLauncher

See Spring-boot executable jar doc and this link

If you do have multiple MainApps which is common, you can use How do I tell Spring Boot which main class to use for the executable jar?

You can add additional locations by setting an environment variable LOADER_PATH or loader.path in loader.properties (comma-separated list of directories, archives, or directories within archives). Basically loader.path works for both java -jar or java -cp way.

And as always you can override and exactly specify the application.yml it should pickup for debugging purpose

--spring.config.location=/some-location/application.yml --debug
1
  • I am using Tomcat9 and deploy my webapp WAR files. I put the application.yml into tomcat top folder but it is not picked up at all. How should I specify the location? Thanks.
    – khteh
    Commented Nov 27, 2018 at 2:57
0

Solution for yml file:

1.Copy yml to in same directory that jar application

2.Run command, example for xxx.yml:

java -jar app.jar --spring.config.location=xxx.yml

It's works fine, but in startup logger is INFO:

No active profile set .........
0

When you are creating spring boot jar using maven install and you want all the resources like properties file and lib folder to be created outside of jar ...then add the following code inside pom.xml where i am defining the output folder where i want to extract and store the desired resources of jar.

<build>
<finalName>project_name_Build/project_name</finalName>
   <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
                <execution>
                    <id>copy-dependencies</id>
                    <phase>prepare-package</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>${project.build.directory}/project_name_Build/lib</outputDirectory>
                        <overWriteReleases>false</overWriteReleases>
                        <overWriteSnapshots>false</overWriteSnapshots>
                        <overWriteIfNewer>true</overWriteIfNewer>
                    </configuration>
                </execution>
            </executions>
        </plugin>

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <classpathPrefix>lib/</classpathPrefix>
                        <mainClass>write here the qualified or complete path of main class of application</mainClass>
                    </manifest>
                    <manifestEntries>
                        <Class-Path>. resources/</Class-Path>
                    </manifestEntries>
                </archive>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
        </plugin>
    </plugins>

    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>application.properties</include>
                <include>log4j.properties</include>
            </includes>
            <targetPath>${project.build.directory}/ConsentGatewayOfflineBuild/resources</targetPath>
        </resource>

        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>application.properties</include>
                <include>log4j.properties</include>
            </includes>
        </resource>

    </resources>

    <pluginManagement>
        <plugins>
            <!-- Ignore/Execute plugin execution -->
            <plugin>
                <groupId>org.eclipse.m2e</groupId>
                <artifactId>lifecycle-mapping</artifactId>
                <version>1.0.0</version>
                <configuration>
                    <lifecycleMappingMetadata>
                        <pluginExecutions>
                            <!-- copy-dependency plugin -->
                            <pluginExecution>
                                <pluginExecutionFilter>
                                    <groupId>org.apache.maven.plugins</groupId>
                                    <artifactId>maven-dependency-plugin</artifactId>
                                    <versionRange>[1.0.0,)</versionRange>
                                    <goals>
                                        <goal>copy-dependencies</goal>
                                    </goals>
                                </pluginExecutionFilter>
                                <action>
                                    <ignore />
                                </action>
                            </pluginExecution>
                        </pluginExecutions>
                    </lifecycleMappingMetadata>
                </configuration>
            </plugin>
        </plugins>
    </pluginManagement>
</build>
0

Place the properties which you want to externalize in a file.properties or yml outside your jar.

@Bean
    //Reads database properties from the config.yml
    public static PropertySourcesPlaceholderConfigurer properties() {
        PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
        YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
        yaml.setResources(new FileSystemResource(filePath + File.separator +"config.yml"));
        propertySourcesPlaceholderConfigurer.setProperties(yaml.getObject());
        propertySourcesPlaceholderConfigurer.setIgnoreResourceNotFound(true);
        return propertySourcesPlaceholderConfigurer;
    }

Not the answer you're looking for? Browse other questions tagged or ask your own question.