Skip to content

Commit eee260f

Browse files
mbhavephilwebb
authored andcommitted
Add support for profile groups
Add support for profile groups so that users can combine a number of fine-grained profiles into a single logical group. Closes gh-22522
1 parent 8c6c4fa commit eee260f

File tree

3 files changed

+101
-1
lines changed

3 files changed

+101
-1
lines changed

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1711,6 +1711,27 @@ For example, when an application with the following properties is run by using t
17111711

17121712

17131713

1714+
[[boot-features-profiles-groups]]
1715+
=== Profile Groups
1716+
Occasionally the profiles that you define and use in your application are too fine-grained and become cumbersome to use.
1717+
For example, you might have `proddb` and `prodmq` profiles that you use to enable database and messaging features independently.
1718+
1719+
To help with this, Spring Boot lets you define profile groups.
1720+
A profile group allows you to define a logical name for a related group of profiles.
1721+
1722+
For example, we can create a `production` group that consists of our `proddb` and `prodmq` profiles.
1723+
1724+
[source,yaml,indent=0]
1725+
----
1726+
spring.profiles.group.production:
1727+
- proddb
1728+
- prodmq
1729+
----
1730+
1731+
Our application can now be started using `--spring.profiles.active=production` to active the `production`, `proddb` and `prodmq` profiles in one hit.
1732+
1733+
1734+
17141735
[[boot-features-programmatically-setting-profiles]]
17151736
=== Programmatically Setting Profiles
17161737
You can programmatically set active profiles by calling `SpringApplication.setAdditionalProfiles(...)` before your application runs.

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,15 @@
2828
import java.util.Set;
2929
import java.util.function.Supplier;
3030

31+
import org.springframework.boot.context.properties.bind.Bindable;
3132
import org.springframework.boot.context.properties.bind.Binder;
33+
import org.springframework.core.ResolvableType;
3234
import org.springframework.core.env.AbstractEnvironment;
3335
import org.springframework.core.env.Environment;
3436
import org.springframework.core.style.ToStringCreator;
3537
import org.springframework.util.CollectionUtils;
38+
import org.springframework.util.LinkedMultiValueMap;
39+
import org.springframework.util.MultiValueMap;
3640
import org.springframework.util.StringUtils;
3741

3842
/**
@@ -45,10 +49,15 @@
4549
*/
4650
public class Profiles implements Iterable<String> {
4751

52+
private static final Bindable<MultiValueMap<String, String>> STRING_STRINGS_MAP = Bindable
53+
.of(ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, String.class));
54+
4855
private static final Set<String> UNSET_ACTIVE = Collections.emptySet();
4956

5057
private static final Set<String> UNSET_DEFAULT = Collections.singleton("default");
5158

59+
private final MultiValueMap<String, String> groups;
60+
5261
private final List<String> activeProfiles;
5362

5463
private final List<String> defaultProfiles;
@@ -63,6 +72,7 @@ public class Profiles implements Iterable<String> {
6372
* @param additionalProfiles and additional active profiles
6473
*/
6574
Profiles(Environment environment, Binder binder, Collection<String> additionalProfiles) {
75+
this.groups = binder.bind("spring.profiles.group", STRING_STRINGS_MAP).orElseGet(LinkedMultiValueMap::new);
6676
this.activeProfiles = asUniqueItemList(get(environment, binder, environment::getActiveProfiles,
6777
AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, UNSET_ACTIVE), additionalProfiles);
6878
this.defaultProfiles = asUniqueItemList(get(environment, binder, environment::getDefaultProfiles,
@@ -94,7 +104,9 @@ private List<String> expandAcceptedProfiles(List<String> activeProfiles, List<St
94104
asReversedList((!activeProfiles.isEmpty()) ? activeProfiles : defaultProfiles).forEach(stack::push);
95105
Set<String> acceptedProfiles = new LinkedHashSet<>();
96106
while (!stack.isEmpty()) {
97-
acceptedProfiles.add(stack.pop());
107+
String current = stack.pop();
108+
acceptedProfiles.add(current);
109+
asReversedList(this.groups.get(current)).forEach(stack::push);
98110
}
99111
return asUniqueItemList(StringUtils.toStringArray(acceptedProfiles));
100112
}

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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,17 @@ void isActiveWhenNoActiveAndDefaultDoesNotContainProfileReturnsFalse() {
262262
assertThat(profiles.isAccepted("x")).isFalse();
263263
}
264264

265+
@Test
266+
void iteratorWithProfileGroups() {
267+
MockEnvironment environment = new MockEnvironment();
268+
environment.setProperty("spring.profiles.active", "a,b,c");
269+
environment.setProperty("spring.profiles.group.a", "e,f");
270+
environment.setProperty("spring.profiles.group.e", "x,y");
271+
Binder binder = Binder.get(environment);
272+
Profiles profiles = new Profiles(environment, binder, null);
273+
assertThat(profiles).containsExactly("a", "e", "x", "y", "f", "b", "c");
274+
}
275+
265276
@Test
266277
void iteratorWithProfileGroupsAndNoActive() {
267278
MockEnvironment environment = new MockEnvironment();
@@ -272,4 +283,60 @@ void iteratorWithProfileGroupsAndNoActive() {
272283
assertThat(profiles).containsExactly("default");
273284
}
274285

286+
@Test
287+
void iteratorWithProfileGroupsForDefault() {
288+
MockEnvironment environment = new MockEnvironment();
289+
environment.setProperty("spring.profiles.group.default", "e,f");
290+
Binder binder = Binder.get(environment);
291+
Profiles profiles = new Profiles(environment, binder, null);
292+
assertThat(profiles).containsExactly("default", "e", "f");
293+
}
294+
295+
@Test
296+
void getAcceptedWithProfileGroups() {
297+
MockEnvironment environment = new MockEnvironment();
298+
environment.setProperty("spring.profiles.active", "a,b,c");
299+
environment.setProperty("spring.profiles.group.a", "e,f");
300+
environment.setProperty("spring.profiles.group.e", "x,y");
301+
environment.setDefaultProfiles("g", "h", "i");
302+
Binder binder = Binder.get(environment);
303+
Profiles profiles = new Profiles(environment, binder, null);
304+
assertThat(profiles.getAccepted()).containsExactly("a", "e", "x", "y", "f", "b", "c");
305+
}
306+
307+
@Test
308+
void getAcceptedWhenNoActiveAndDefaultWithGroups() {
309+
MockEnvironment environment = new MockEnvironment();
310+
environment.setDefaultProfiles("d", "e", "f");
311+
environment.setProperty("spring.profiles.group.e", "x,y");
312+
Binder binder = Binder.get(environment);
313+
Profiles profiles = new Profiles(environment, binder, null);
314+
assertThat(profiles.getAccepted()).containsExactly("d", "e", "x", "y", "f");
315+
}
316+
317+
@Test
318+
void isAcceptedWithGroupsReturnsTrue() {
319+
MockEnvironment environment = new MockEnvironment();
320+
environment.setProperty("spring.profiles.active", "a,b,c");
321+
environment.setProperty("spring.profiles.group.a", "e,f");
322+
environment.setProperty("spring.profiles.group.e", "x,y");
323+
environment.setDefaultProfiles("g", "h", "i");
324+
Binder binder = Binder.get(environment);
325+
Profiles profiles = new Profiles(environment, binder, null);
326+
assertThat(profiles.isAccepted("a")).isTrue();
327+
assertThat(profiles.isAccepted("e")).isTrue();
328+
assertThat(profiles.isAccepted("g")).isFalse();
329+
}
330+
331+
@Test
332+
void isAcceptedWhenNoActiveAndDefaultWithGroupsContainsProfileReturnsTrue() {
333+
MockEnvironment environment = new MockEnvironment();
334+
environment.setDefaultProfiles("d", "e", "f");
335+
environment.setProperty("spring.profiles.group.e", "x,y");
336+
Binder binder = Binder.get(environment);
337+
Profiles profiles = new Profiles(environment, binder, null);
338+
assertThat(profiles.isAccepted("d")).isTrue();
339+
assertThat(profiles.isAccepted("x")).isTrue();
340+
}
341+
275342
}

0 commit comments

Comments
 (0)