[spring] Spring Boot: Is it possible to use external application.properties files in arbitrary directories with a fat jar?

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

This question is related to spring configuration spring-boot

The answer is


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

<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

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

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!


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.


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.


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

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>

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.


java -jar server-0.0.1-SNAPSHOT.jar --spring.config.location=application-prod.properties

Examples related to spring

Are all Spring Framework Java Configuration injection examples buggy? Two Page Login with Spring Security 3.2.x Access blocked by CORS policy: Response to preflight request doesn't pass access control check Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean Failed to auto-configure a DataSource: 'spring.datasource.url' is not specified Spring Data JPA findOne() change to Optional how to use this? After Spring Boot 2.0 migration: jdbcUrl is required with driverClassName The type WebMvcConfigurerAdapter is deprecated No converter found capable of converting from type to type

Examples related to configuration

My eclipse won't open, i download the bundle pack it keeps saying error log Getting value from appsettings.json in .net core pgadmin4 : postgresql application server could not be contacted. ASP.NET Core configuration for .NET Core console application Turning off eslint rule for a specific file PHP Warning: Module already loaded in Unknown on line 0 How to read AppSettings values from a .json file in ASP.NET Core How to store Configuration file and read it using React Hadoop cluster setup - java.net.ConnectException: Connection refused Maven Jacoco Configuration - Exclude classes/packages from report not working

Examples related to spring-boot

Access blocked by CORS policy: Response to preflight request doesn't pass access control check Why am I getting Unknown error in line 1 of pom.xml? Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured How to resolve Unable to load authentication plugin 'caching_sha2_password' issue ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean Failed to auto-configure a DataSource: 'spring.datasource.url' is not specified After Spring Boot 2.0 migration: jdbcUrl is required with driverClassName ERROR Source option 1.5 is no longer supported. Use 1.6 or later How to start up spring-boot application via command line? JSON parse error: Can not construct instance of java.time.LocalDate: no String-argument constructor/factory method to deserialize from String value