Skip to content

Commit a89b2ae

Browse files
committed
Support profile expression in yml profile matching
Closes gh-12469
1 parent 0c4176f commit a89b2ae

File tree

7 files changed

+90
-33
lines changed

7 files changed

+90
-33
lines changed

spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -756,15 +756,25 @@ example:
756756
address: 127.0.0.1
757757
---
758758
spring:
759-
profiles: production
759+
profiles: production & eu-central
760760
server:
761761
address: 192.168.1.120
762762
----
763763

764764
In the preceding example, if the `development` profile is active, the `server.address`
765-
property is `127.0.0.1`. Similarly, if the `production` profile is active, the
766-
`server.address` property is `192.168.1.120`. If the `development` and `production`
767-
profiles are *not* enabled, then the value for the property is `192.168.1.100`.
765+
property is `127.0.0.1`. Similarly, if the `production` *and* `eu-central` profiles are
766+
active, the `server.address` property is `192.168.1.120`. If the `development` and
767+
`production` and `eu-central` profiles are *not* enabled, then the value for the property
768+
is `192.168.1.100`.
769+
770+
[NOTE]
771+
====
772+
`spring.profiles` can therefore contains a simple profile name (for example `production`)
773+
or a profile expression. A profile expression allows for more complicated profile logic
774+
to be expressed, for example `production & (eu-central | eu-west)`. Check the
775+
{spring-reference}core.html#beans-definition-profiles-java[reference guide] for more
776+
details.
777+
====
768778

769779
If none are explicitly active when the application context starts, the default profiles
770780
are activated. So, in the following YAML, we set a value for `spring.security.user.password`

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import org.springframework.core.env.ConfigurableEnvironment;
5858
import org.springframework.core.env.Environment;
5959
import org.springframework.core.env.MutablePropertySources;
60+
import org.springframework.core.env.Profiles;
6061
import org.springframework.core.env.PropertySource;
6162
import org.springframework.core.io.DefaultResourceLoader;
6263
import org.springframework.core.io.Resource;
@@ -411,14 +412,15 @@ private DocumentFilter getPositiveProfileFilter(Profile profile) {
411412
}
412413
return ObjectUtils.containsElement(document.getProfiles(),
413414
profile.getName())
414-
&& this.environment.acceptsProfiles(document.getProfiles());
415+
&& this.environment
416+
.acceptsProfiles(Profiles.of(document.getProfiles()));
415417
};
416418
}
417419

418420
private DocumentFilter getNegativeProfileFilter(Profile profile) {
419421
return (Document document) -> (profile == null
420-
&& !ObjectUtils.isEmpty(document.getProfiles())
421-
&& this.environment.acceptsProfiles(document.getProfiles()));
422+
&& !ObjectUtils.isEmpty(document.getProfiles()) && this.environment
423+
.acceptsProfiles(Profiles.of(document.getProfiles())));
422424
}
423425

424426
private DocumentConsumer addToLoaded(

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ReproTests.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.springframework.context.ConfigurableApplicationContext;
2323
import org.springframework.context.annotation.Configuration;
24+
import org.springframework.core.env.Profiles;
2425

2526
import static org.assertj.core.api.Assertions.assertThat;
2627

@@ -50,8 +51,10 @@ public void enableProfileViaApplicationProperties() {
5051
this.context = application.run(
5152
"--spring.config.name=enableprofileviaapplicationproperties",
5253
"--spring.profiles.active=dev");
53-
assertThat(this.context.getEnvironment().acceptsProfiles("dev")).isTrue();
54-
assertThat(this.context.getEnvironment().acceptsProfiles("a")).isTrue();
54+
assertThat(this.context.getEnvironment().acceptsProfiles(Profiles.of("dev")))
55+
.isTrue();
56+
assertThat(this.context.getEnvironment().acceptsProfiles(Profiles.of("a")))
57+
.isTrue();
5558
}
5659

5760
@Test

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
import org.springframework.core.env.ConfigurableEnvironment;
8484
import org.springframework.core.env.Environment;
8585
import org.springframework.core.env.MapPropertySource;
86+
import org.springframework.core.env.Profiles;
8687
import org.springframework.core.env.PropertySource;
8788
import org.springframework.core.env.StandardEnvironment;
8889
import org.springframework.core.io.ClassPathResource;
@@ -525,7 +526,7 @@ public void addProfiles() {
525526
ConfigurableEnvironment environment = new StandardEnvironment();
526527
application.setEnvironment(environment);
527528
this.context = application.run();
528-
assertThat(environment.acceptsProfiles("foo")).isTrue();
529+
assertThat(environment.acceptsProfiles(Profiles.of("foo"))).isTrue();
529530
}
530531

531532
@Test

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.context.annotation.Configuration;
3232
import org.springframework.context.support.StaticApplicationContext;
3333
import org.springframework.core.env.ConfigurableEnvironment;
34+
import org.springframework.core.env.Profiles;
3435
import org.springframework.core.env.StandardEnvironment;
3536
import org.springframework.core.io.DefaultResourceLoader;
3637
import org.springframework.core.io.ResourceLoader;
@@ -72,7 +73,8 @@ public void profileAndProperties() {
7273
this.context = application.run();
7374
assertThat(this.context).isInstanceOf(StaticApplicationContext.class);
7475
assertThat(this.context.getEnvironment().getProperty("foo")).isEqualTo("bucket");
75-
assertThat(this.context.getEnvironment().acceptsProfiles("foo")).isTrue();
76+
assertThat(this.context.getEnvironment().acceptsProfiles(Profiles.of("foo")))
77+
.isTrue();
7678
}
7779

7880
@Test
@@ -205,11 +207,12 @@ public void parentFirstCreationWithProfileAndDefaultArgs() {
205207
ExampleConfig.class).profiles("node").properties("transport=redis")
206208
.child(ChildConfig.class).web(WebApplicationType.NONE);
207209
this.context = application.run();
208-
assertThat(this.context.getEnvironment().acceptsProfiles("node")).isTrue();
210+
assertThat(this.context.getEnvironment().acceptsProfiles(Profiles.of("node")))
211+
.isTrue();
209212
assertThat(this.context.getEnvironment().getProperty("transport"))
210213
.isEqualTo("redis");
211-
assertThat(this.context.getParent().getEnvironment().acceptsProfiles("node"))
212-
.isTrue();
214+
assertThat(this.context.getParent().getEnvironment()
215+
.acceptsProfiles(Profiles.of("node"))).isTrue();
213216
assertThat(this.context.getParent().getEnvironment().getProperty("transport"))
214217
.isEqualTo("redis");
215218
// only defined in node profile
@@ -223,10 +226,10 @@ public void parentFirstWithDifferentProfile() {
223226
.child(ChildConfig.class).profiles("admin")
224227
.web(WebApplicationType.NONE);
225228
this.context = application.run();
226-
assertThat(this.context.getEnvironment().acceptsProfiles("node", "admin"))
227-
.isTrue();
228-
assertThat(this.context.getParent().getEnvironment().acceptsProfiles("admin"))
229-
.isFalse();
229+
assertThat(this.context.getEnvironment()
230+
.acceptsProfiles(Profiles.of("node", "admin"))).isTrue();
231+
assertThat(this.context.getParent().getEnvironment()
232+
.acceptsProfiles(Profiles.of("admin"))).isFalse();
230233
}
231234

232235
@Test
@@ -237,12 +240,12 @@ public void parentWithDifferentProfile() {
237240
.profiles("admin").web(WebApplicationType.NONE);
238241
shared.profiles("parent");
239242
this.context = application.run();
240-
assertThat(this.context.getEnvironment().acceptsProfiles("node", "admin"))
241-
.isTrue();
242-
assertThat(this.context.getParent().getEnvironment().acceptsProfiles("node",
243-
"parent")).isTrue();
244-
assertThat(this.context.getParent().getEnvironment().acceptsProfiles("admin"))
245-
.isFalse();
243+
assertThat(this.context.getEnvironment()
244+
.acceptsProfiles(Profiles.of("node", "admin"))).isTrue();
245+
assertThat(this.context.getParent().getEnvironment()
246+
.acceptsProfiles(Profiles.of("node", "parent"))).isTrue();
247+
assertThat(this.context.getParent().getEnvironment()
248+
.acceptsProfiles(Profiles.of("admin"))).isFalse();
246249
}
247250

248251
@Test
@@ -253,12 +256,12 @@ public void parentFirstWithDifferentProfileAndExplicitEnvironment() {
253256
.child(ChildConfig.class).profiles("admin")
254257
.web(WebApplicationType.NONE);
255258
this.context = application.run();
256-
assertThat(this.context.getEnvironment().acceptsProfiles("node", "admin"))
257-
.isTrue();
259+
assertThat(this.context.getEnvironment()
260+
.acceptsProfiles(Profiles.of("node", "admin"))).isTrue();
258261
// Now they share an Environment explicitly so there's no way to keep the profiles
259262
// separate
260-
assertThat(this.context.getParent().getEnvironment().acceptsProfiles("admin"))
261-
.isTrue();
263+
assertThat(this.context.getParent().getEnvironment()
264+
.acceptsProfiles(Profiles.of("admin"))).isTrue();
262265
}
263266

264267
@Test

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import org.springframework.core.annotation.Order;
5555
import org.springframework.core.env.ConfigurableEnvironment;
5656
import org.springframework.core.env.MapPropertySource;
57+
import org.springframework.core.env.Profiles;
5758
import org.springframework.core.env.SimpleCommandLinePropertySource;
5859
import org.springframework.core.env.StandardEnvironment;
5960
import org.springframework.core.io.ByteArrayResource;
@@ -504,6 +505,29 @@ public void yamlTwoProfiles() {
504505
assertThat(property).isEqualTo("notempty");
505506
}
506507

508+
@Test
509+
public void yamlProfileExpressionsAnd() {
510+
assertProfileExpression("devandother", "dev", "other");
511+
}
512+
513+
@Test
514+
public void yamlProfileExpressionsComplex() {
515+
assertProfileExpression("devorotherandanother", "dev", "another");
516+
}
517+
518+
@Test
519+
public void yamlProfileExpressionsNoMatch() {
520+
assertProfileExpression("fromyamlfile", "dev");
521+
}
522+
523+
private void assertProfileExpression(String value, String... activeProfiles) {
524+
this.environment.setActiveProfiles(activeProfiles);
525+
this.initializer.setSearchNames("testprofileexpression");
526+
this.initializer.postProcessEnvironment(this.environment, this.application);
527+
String property = this.environment.getProperty("my.property");
528+
assertThat(property).isEqualTo(value);
529+
}
530+
507531
@Test
508532
public void yamlNegatedProfiles() {
509533
// gh-8011
@@ -827,7 +851,7 @@ public void customDefaultProfileAndActiveFromFile() {
827851
assertThat(environment.containsProperty("customprofile")).isTrue();
828852
assertThat(environment.containsProperty("customprofile-specific")).isTrue();
829853
assertThat(environment.containsProperty("customprofile-customdefault")).isTrue();
830-
assertThat(environment.acceptsProfiles("customdefault")).isTrue();
854+
assertThat(environment.acceptsProfiles(Profiles.of("customdefault"))).isTrue();
831855
}
832856

833857
@Test
@@ -899,7 +923,7 @@ public void includeLoop() {
899923
application.setWebApplicationType(WebApplicationType.NONE);
900924
this.context = application.run("--spring.config.name=applicationloop");
901925
ConfigurableEnvironment environment = this.context.getEnvironment();
902-
assertThat(environment.acceptsProfiles("loop")).isTrue();
926+
assertThat(environment.acceptsProfiles(Profiles.of("loop"))).isTrue();
903927
}
904928

905929
@Test
@@ -909,8 +933,8 @@ public void multiValueSpringProfiles() {
909933
application.setWebApplicationType(WebApplicationType.NONE);
910934
this.context = application.run("--spring.config.name=applicationmultiprofiles");
911935
ConfigurableEnvironment environment = this.context.getEnvironment();
912-
assertThat(environment.acceptsProfiles("test")).isTrue();
913-
assertThat(environment.acceptsProfiles("another-test")).isTrue();
936+
assertThat(environment.acceptsProfiles(Profiles.of("test"))).isTrue();
937+
assertThat(environment.acceptsProfiles(Profiles.of("another-test"))).isTrue();
914938
assertThat(environment.getProperty("message")).isEqualTo("multiprofile");
915939
}
916940

@@ -932,7 +956,7 @@ private Condition<ConfigurableEnvironment> matchingProfile(String profile) {
932956

933957
@Override
934958
public boolean matches(ConfigurableEnvironment value) {
935-
return value.acceptsProfiles(profile);
959+
return value.acceptsProfiles(Profiles.of(profile));
936960
}
937961

938962
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
my:
3+
property: fromyamlfile
4+
---
5+
spring:
6+
profiles: dev & other
7+
my:
8+
property: devandother
9+
---
10+
spring:
11+
profiles: (dev | other) & another
12+
my:
13+
property: devorotherandanother
14+
---

0 commit comments

Comments
 (0)