commit
abd926788f
|
|
@ -103,25 +103,29 @@ This means that the JSON cannot override properties from lower order property so
|
|||
=== External Application Properties [[features.external-config.files]]
|
||||
Spring Boot will automatically find and load `application.properties` and `application.yaml` files from the following locations when your application starts:
|
||||
|
||||
. The classpath root
|
||||
. The classpath `/config` package
|
||||
. The current directory
|
||||
. The `/config` subdirectory in the current directory
|
||||
. Immediate child directories of the `/config` subdirectory
|
||||
. From the classpath
|
||||
.. The classpath root
|
||||
.. The classpath `/config` package
|
||||
. From the current directory
|
||||
.. The current directory
|
||||
.. The `/config` subdirectory in the current directory
|
||||
.. Immediate child directories of the `/config` subdirectory
|
||||
|
||||
The list is ordered by precedence (with values from lower items overriding earlier ones).
|
||||
Documents from the loaded files are added as `PropertySources` to the Spring `Environment`.
|
||||
|
||||
If you do not like `application` as the configuration file name, you can switch to another file name by specifying a configprop:spring.config.name[] environment property.
|
||||
You can also refer to an explicit location by using the `spring.config.location` environment property (which is a comma-separated list of directory locations or file paths).
|
||||
The following example shows how to specify a different file name:
|
||||
For example, to look for `myproject.properties` and `myproject.yaml` files you can run your application as follows:
|
||||
|
||||
[source,shell,indent=0,subs="verbatim"]
|
||||
----
|
||||
$ java -jar myproject.jar --spring.config.name=myproject
|
||||
----
|
||||
|
||||
The following example shows how to specify two locations:
|
||||
You can also refer to an explicit location by using the configprop:spring.config.location[] environment property.
|
||||
This properties accepts a comma-separated list of one or more locations to check.
|
||||
|
||||
The following example shows how to specify two distinct files:
|
||||
|
||||
[source,shell,indent=0,subs="verbatim"]
|
||||
----
|
||||
|
|
@ -135,14 +139,19 @@ TIP: Use the prefix `optional:` if the <<features#features.external-config.files
|
|||
WARNING: `spring.config.name`, `spring.config.location`, and `spring.config.additional-location` are used very early to determine which files have to be loaded.
|
||||
They must be defined as an environment property (typically an OS environment variable, a system property, or a command-line argument).
|
||||
|
||||
If `spring.config.location` contains directories (as opposed to files), they must end in `/` or the system-dependent `File.separator`.
|
||||
If `spring.config.location` contains directories (as opposed to files), they should end in `/`.
|
||||
At runtime they will be appended with the names generated from `spring.config.name` before being loaded.
|
||||
If `spring.config.location` contains files, they are used as-is.
|
||||
Files specified in `spring.config.location` are used as-is.
|
||||
|
||||
Whether specified directly or contained in a directory, file references must include a file extension in their name.
|
||||
Typical extensions that are supported out-of-the-box are `.properties`, `.yaml`, and `.yml`.
|
||||
In most situations, each configprop:spring.config.location[] item you add will reference a single file or directory.
|
||||
Locations are processed in the order that they are defined and later ones can override the values of earlier ones.
|
||||
|
||||
When multiple locations are specified, the later ones can override the values of earlier ones.
|
||||
[[features.external-config.files.location-groups]]
|
||||
If you have a complex location setup, and you use profile-specific configuration files, you may need to provide further hints so that Spring Boot knows how they should be grouped.
|
||||
A location group is a collection of locations that are all considered at the same level.
|
||||
For example, you might want to group all classpath locations, then all external locations.
|
||||
Items within a location group should be separated with `;`.
|
||||
See the example in the "`<<features.external-config.files.profile-specific>>`" section for more details.
|
||||
|
||||
Locations configured by using `spring.config.location` replace the default locations.
|
||||
For example, if `spring.config.location` is configured with the value `optional:classpath:/custom-config/,optional:file:./custom-config/`, the complete set of locations considered is:
|
||||
|
|
@ -154,11 +163,8 @@ If you prefer to add additional locations, rather than replacing them, you can u
|
|||
Properties loaded from additional locations can override those in the default locations.
|
||||
For example, if `spring.config.additional-location` is configured with the value `optional:classpath:/custom-config/,optional:file:./custom-config/`, the complete set of locations considered is:
|
||||
|
||||
. `optional:classpath:/`
|
||||
. `optional:classpath:/config/`
|
||||
. `optional:file:./`
|
||||
. `optional:file:./config/`
|
||||
. `optional:file:./config/*/`
|
||||
. `optional:classpath:/;optional:classpath:/config/`
|
||||
. `optional:file:./;optional:file:./config/;optional:file:./config/*/`
|
||||
. `optional:classpath:custom-config/`
|
||||
. `optional:file:./custom-config/`
|
||||
|
||||
|
|
@ -219,6 +225,35 @@ Profile-specific properties are loaded from the same locations as standard `appl
|
|||
If several profiles are specified, a last-wins strategy applies.
|
||||
For example, if profiles `prod,live` are specified by the configprop:spring.profiles.active[] property, values in `application-prod.properties` can be overridden by those in `application-live.properties`.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
The last-wins strategy applies at the <<features.external-config.files.location-groups,location group>> level.
|
||||
A configprop:spring.config.location[] of `classpath:/cfg/,classpath:/ext/` will not have the same override rules as `classpath:/cfg/;classpath:/ext/`.
|
||||
|
||||
For example, continuing our `prod,live` example above, we might have the following files:
|
||||
|
||||
----
|
||||
/cfg
|
||||
application-live.properties
|
||||
/ext
|
||||
application-live.properties
|
||||
application-prod.properties
|
||||
----
|
||||
|
||||
When we have a configprop:spring.config.location[] of `classpath:/cfg/,classpath:/ext/` we process all `/cfg` files before all `/ext` files:
|
||||
|
||||
. `/cfg/application-live.properties`
|
||||
. `/ext/application-prod.properties`
|
||||
. `/ext/application-live.properties`
|
||||
|
||||
|
||||
When we have `classpath:/cfg/;classpath:/ext/` instead (with a `;` delimiter) we process `/cfg` and `/ext` at the same level:
|
||||
|
||||
. `/ext/application-prod.properties`
|
||||
. `/cfg/application-live.properties`
|
||||
. `/ext/application-live.properties`
|
||||
====
|
||||
|
||||
The `Environment` has a set of default profiles (by default, `[default]`) that are used if no active profiles are set.
|
||||
In other words, if no profiles are explicitly activated, then properties from `application-default` are considered.
|
||||
|
||||
|
|
|
|||
|
|
@ -87,11 +87,8 @@ class ConfigDataEnvironment {
|
|||
static final ConfigDataLocation[] DEFAULT_SEARCH_LOCATIONS;
|
||||
static {
|
||||
List<ConfigDataLocation> locations = new ArrayList<>();
|
||||
locations.add(ConfigDataLocation.of("optional:classpath:/"));
|
||||
locations.add(ConfigDataLocation.of("optional:classpath:/config/"));
|
||||
locations.add(ConfigDataLocation.of("optional:file:./"));
|
||||
locations.add(ConfigDataLocation.of("optional:file:./config/"));
|
||||
locations.add(ConfigDataLocation.of("optional:file:./config/*/"));
|
||||
locations.add(ConfigDataLocation.of("optional:classpath:/;optional:classpath:/config/"));
|
||||
locations.add(ConfigDataLocation.of("optional:file:./;optional:file:./config/;optional:file:./config/*/"));
|
||||
DEFAULT_SEARCH_LOCATIONS = locations.toArray(new ConfigDataLocation[0]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,6 +97,32 @@ public final class ConfigDataLocation implements OriginProvider {
|
|||
return this.origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of {@link ConfigDataLocation} elements built by splitting this
|
||||
* {@link ConfigDataLocation} around a delimiter of {@code ";"}.
|
||||
* @return the split locations
|
||||
* @since 2.4.7
|
||||
*/
|
||||
public ConfigDataLocation[] split() {
|
||||
return split(";");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of {@link ConfigDataLocation} elements built by splitting this
|
||||
* {@link ConfigDataLocation} around the specified delimiter.
|
||||
* @param delimiter the delimiter to split on
|
||||
* @return the split locations
|
||||
* @since 2.4.7
|
||||
*/
|
||||
public ConfigDataLocation[] split(String delimiter) {
|
||||
String[] values = StringUtils.delimitedListToStringArray(toString(), delimiter);
|
||||
ConfigDataLocation[] result = new ConfigDataLocation[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
result[i] = of(values[i]).withOrigin(getOrigin());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
|
|
|
|||
|
|
@ -116,7 +116,16 @@ public class StandardConfigDataLocationResolver
|
|||
@Override
|
||||
public List<StandardConfigDataResource> resolve(ConfigDataLocationResolverContext context,
|
||||
ConfigDataLocation location) throws ConfigDataNotFoundException {
|
||||
return resolve(getReferences(context, location));
|
||||
return resolve(getReferences(context, location.split()));
|
||||
}
|
||||
|
||||
private Set<StandardConfigDataReference> getReferences(ConfigDataLocationResolverContext context,
|
||||
ConfigDataLocation[] configDataLocations) {
|
||||
Set<StandardConfigDataReference> references = new LinkedHashSet<>();
|
||||
for (ConfigDataLocation configDataLocation : configDataLocations) {
|
||||
references.addAll(getReferences(context, configDataLocation));
|
||||
}
|
||||
return references;
|
||||
}
|
||||
|
||||
private Set<StandardConfigDataReference> getReferences(ConfigDataLocationResolverContext context,
|
||||
|
|
@ -139,15 +148,17 @@ public class StandardConfigDataLocationResolver
|
|||
if (context.getParent() != null) {
|
||||
return null;
|
||||
}
|
||||
return resolve(getProfileSpecificReferences(context, location, profiles));
|
||||
return resolve(getProfileSpecificReferences(context, location.split(), profiles));
|
||||
}
|
||||
|
||||
private Set<StandardConfigDataReference> getProfileSpecificReferences(ConfigDataLocationResolverContext context,
|
||||
ConfigDataLocation configDataLocation, Profiles profiles) {
|
||||
ConfigDataLocation[] configDataLocations, Profiles profiles) {
|
||||
Set<StandardConfigDataReference> references = new LinkedHashSet<>();
|
||||
String resourceLocation = getResourceLocation(context, configDataLocation);
|
||||
for (String profile : profiles) {
|
||||
references.addAll(getReferences(configDataLocation, resourceLocation, profile));
|
||||
for (ConfigDataLocation configDataLocation : configDataLocations) {
|
||||
String resourceLocation = getResourceLocation(context, configDataLocation);
|
||||
references.addAll(getReferences(configDataLocation, resourceLocation, profile));
|
||||
}
|
||||
}
|
||||
return references;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -772,6 +772,21 @@ class ConfigDataEnvironmentPostProcessorIntegrationTests {
|
|||
assertThat(environment.containsProperty("test:boot:ps")).isFalse();
|
||||
}
|
||||
|
||||
@Test // gh-26593
|
||||
void runWhenHasFilesInRootAndConfigWithProfiles() {
|
||||
ConfigurableApplicationContext context = this.application
|
||||
.run("--spring.config.name=file-in-root-and-config-with-profile", "--spring.profiles.active=p1,p2");
|
||||
ConfigurableEnvironment environment = context.getEnvironment();
|
||||
assertThat(environment.containsProperty("file-in-root-and-config-with-profile")).isTrue();
|
||||
assertThat(environment.containsProperty("file-in-root-and-config-with-profile-p1")).isTrue();
|
||||
assertThat(environment.containsProperty("file-in-root-and-config-with-profile-p2")).isTrue();
|
||||
assertThat(environment.containsProperty("config-file-in-root-and-config-with-profile")).isTrue();
|
||||
assertThat(environment.containsProperty("config-file-in-root-and-config-with-profile-p1")).isTrue();
|
||||
assertThat(environment.containsProperty("config-file-in-root-and-config-with-profile-p2")).isTrue();
|
||||
assertThat(environment.getProperty("v1")).isEqualTo("config-file-in-root-and-config-with-profile-p2");
|
||||
assertThat(environment.getProperty("v2")).isEqualTo("file-in-root-and-config-with-profile-p2");
|
||||
}
|
||||
|
||||
private Condition<ConfigurableEnvironment> matchingPropertySource(final String sourceName) {
|
||||
return new Condition<ConfigurableEnvironment>("environment containing property source " + sourceName) {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -134,4 +134,36 @@ class ConfigDataLocationTests {
|
|||
assertThat(ConfigDataLocation.of("test")).hasToString("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void splitWhenNoSemiColonReturnsSingleElement() {
|
||||
ConfigDataLocation location = ConfigDataLocation.of("test");
|
||||
ConfigDataLocation[] split = location.split();
|
||||
assertThat(split).containsExactly(ConfigDataLocation.of("test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void splitWhenSemiColonReturnsElements() {
|
||||
ConfigDataLocation location = ConfigDataLocation.of("one;two;three");
|
||||
ConfigDataLocation[] split = location.split();
|
||||
assertThat(split).containsExactly(ConfigDataLocation.of("one"), ConfigDataLocation.of("two"),
|
||||
ConfigDataLocation.of("three"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void splitOnCharReturnsElements() {
|
||||
ConfigDataLocation location = ConfigDataLocation.of("one::two::three");
|
||||
ConfigDataLocation[] split = location.split("::");
|
||||
assertThat(split).containsExactly(ConfigDataLocation.of("one"), ConfigDataLocation.of("two"),
|
||||
ConfigDataLocation.of("three"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void splitWhenHasOriginReturnsElementsWithOriginSet() {
|
||||
Origin origin = mock(Origin.class);
|
||||
ConfigDataLocation location = ConfigDataLocation.of("a;b").withOrigin(origin);
|
||||
ConfigDataLocation[] split = location.split();
|
||||
assertThat(split[0].getOrigin()).isEqualTo(origin);
|
||||
assertThat(split[1].getOrigin()).isEqualTo(origin);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
config-file-in-root-and-config-with-profile-p1=true
|
||||
v1=config-file-in-root-and-config-with-profile-p1
|
||||
#v2 intentionally missing
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
config-file-in-root-and-config-with-profile-p2=true
|
||||
v1=config-file-in-root-and-config-with-profile-p2
|
||||
#v2 intentionally missing
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
config-file-in-root-and-config-with-profile=true
|
||||
v1=config-file-in-root-and-config-with-profile
|
||||
v2=config-file-in-root-and-config-with-profile
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
file-in-root-and-config-with-profile-p1=true
|
||||
v1=file-in-root-and-config-with-profile-p1
|
||||
v2=file-in-root-and-config-with-profile-p1
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
file-in-root-and-config-with-profile-p2=true
|
||||
v1=file-in-root-and-config-with-profile-p2
|
||||
v2=file-in-root-and-config-with-profile-p2
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
file-in-root-and-config-with-profile=true
|
||||
v1=file-in-root-and-config-with-profile
|
||||
v2=file-in-root-and-config-with-profile
|
||||
Loading…
Reference in New Issue