Skip to content

Commit 2e31447

Browse files
committed
GH-1009 - Allow customizing the detection of NamedInterfaces via ApplicationModuleDetectionStrategy.
The detection of NamedInterfaces for an application module can now be customized by implementing ApplicationModuleDetectionStrategy.detectNamedInterfaces(…). The default implementation of that methods uses the previously default NamedInterfaces.of(…) lookup. NamedInterfaces and NamedInterface now expose factory methods to be able to construct and combine (through NamedInterfaces.and(…)) instances manually. In particular, the newly introduced NamedInterfaces.builder() method allows setting up a detection configuration that allows picking up packages by naming conventions.
1 parent f941756 commit 2e31447

File tree

11 files changed

+458
-18
lines changed

11 files changed

+458
-18
lines changed

spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModule.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,7 @@ public class ApplicationModule implements Comparable<ApplicationModule> {
109109
this.exclusions = exclusions;
110110
this.classes = basePackage.getClasses(exclusions);
111111
this.information = ApplicationModuleInformation.of(basePackage);
112-
this.namedInterfaces = isOpen()
113-
? NamedInterfaces.forOpen(basePackage)
114-
: NamedInterfaces.discoverNamedInterfaces(basePackage);
112+
this.namedInterfaces = source.getNamedInterfaces(information);
115113

116114
this.springBeans = SingletonSupplier.of(() -> filterSpringBeans(classes));
117115
this.aggregateRoots = SingletonSupplier.of(() -> findAggregateRoots(classes));

spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModuleDetectionStrategy.java

+20-3
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,30 @@
2929
public interface ApplicationModuleDetectionStrategy {
3030

3131
/**
32-
* Given the {@link JavaPackage} that Moduliths was initialized with, return the base packages for all modules in the
33-
* system.
32+
* Given the {@link JavaPackage} that Spring Modulith was initialized with, return the base packages that are supposed
33+
* to be considered base packages for {@link ApplicationModule}s.
34+
*
35+
* @param rootPackage will never be {@literal null}.
36+
* @return must not be {@literal null}.
37+
*/
38+
Stream<JavaPackage> getModuleBasePackages(JavaPackage rootPackage);
39+
40+
/**
41+
* Optionally customize the detection of {@link NamedInterfaces} for a module with the given base package and the
42+
* pre-obtained {@link ApplicationModuleInformation}. Defaults to
43+
* {@link NamedInterfaces#of(JavaPackage, ApplicationModuleInformation)}. {@link NamedInterfaces} exposes a
44+
* {@link NamedInterfaces.Builder} API to define the selection of packages to be considered named interfaces.
3445
*
3546
* @param basePackage will never be {@literal null}.
47+
* @param information will never be {@literal null}.
3648
* @return must not be {@literal null}.
49+
* @see NamedInterfaces#of(JavaPackage, ApplicationModuleInformation)
50+
* @see NamedInterfaces#builder(JavaPackage)
51+
* @since 1.4
3752
*/
38-
Stream<JavaPackage> getModuleBasePackages(JavaPackage basePackage);
53+
default NamedInterfaces detectNamedInterfaces(JavaPackage basePackage, ApplicationModuleInformation information) {
54+
return NamedInterfaces.of(basePackage, information);
55+
}
3956

4057
/**
4158
* A {@link ApplicationModuleDetectionStrategy} that considers all direct sub-packages of the Moduliths base package

spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModuleSource.java

+24-3
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,25 @@ public class ApplicationModuleSource {
4545

4646
private final JavaPackage moduleBasePackage;
4747
private final ApplicationModuleIdentifier identifier;
48+
private final Function<ApplicationModuleInformation, NamedInterfaces> namedInterfacesFactory;
4849

4950
/**
5051
* Creates a new {@link ApplicationModuleSource} for the given module base package and module name.
5152
*
5253
* @param moduleBasePackage must not be {@literal null}.
5354
* @param moduleName must not be {@literal null} or empty.
55+
* @param namedInterfacesFactory must not be {@literal null}.
5456
*/
55-
private ApplicationModuleSource(JavaPackage moduleBasePackage, ApplicationModuleIdentifier identifier) {
57+
private ApplicationModuleSource(JavaPackage moduleBasePackage, ApplicationModuleIdentifier identifier,
58+
Function<ApplicationModuleInformation, NamedInterfaces> namedInterfacesFactory) {
5659

5760
Assert.notNull(moduleBasePackage, "JavaPackage must not be null!");
61+
Assert.notNull(identifier, "ApplicationModuleIdentifier must not be null!");
62+
Assert.notNull(namedInterfacesFactory, "NamedInterfaces factory must not be null!");
5863

5964
this.moduleBasePackage = moduleBasePackage;
6065
this.identifier = identifier;
66+
this.namedInterfacesFactory = namedInterfacesFactory;
6167
}
6268

6369
/**
@@ -83,7 +89,7 @@ public static Stream<ApplicationModuleSource> from(JavaPackage rootPackage,
8389
.orElseGet(() -> ApplicationModuleIdentifier.of(
8490
fullyQualifiedModuleNames ? it.getName() : rootPackage.getTrailingName(it)));
8591

86-
return new ApplicationModuleSource(it, id);
92+
return new ApplicationModuleSource(it, id, (info) -> strategy.detectNamedInterfaces(it, info));
8793
});
8894
}
8995

@@ -95,7 +101,8 @@ public static Stream<ApplicationModuleSource> from(JavaPackage rootPackage,
95101
* @return will never be {@literal null}.
96102
*/
97103
static ApplicationModuleSource from(JavaPackage pkg, String identifier) {
98-
return new ApplicationModuleSource(pkg, ApplicationModuleIdentifier.of(identifier));
104+
return new ApplicationModuleSource(pkg, ApplicationModuleIdentifier.of(identifier),
105+
(info) -> NamedInterfaces.of(pkg, info));
99106
}
100107

101108
/**
@@ -116,6 +123,20 @@ public ApplicationModuleIdentifier getIdentifier() {
116123
return identifier;
117124
}
118125

126+
/**
127+
* Returns all {@link NamedInterfaces} for the given {@link ApplicationModuleInformation}.
128+
*
129+
* @param information must not be {@literal null}.
130+
* @return will never be {@literal null}.
131+
* @since 1.4
132+
*/
133+
public NamedInterfaces getNamedInterfaces(ApplicationModuleInformation information) {
134+
135+
Assert.notNull(information, "ApplicationModuleInformation must not be null!");
136+
137+
return namedInterfacesFactory.apply(information);
138+
}
139+
119140
/*
120141
* (non-Javadoc)
121142
* @see java.lang.Object#equals(java.lang.Object)

spring-modulith-core/src/main/java/org/springframework/modulith/core/JavaPackage.java

+18
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.Objects;
2929
import java.util.Optional;
3030
import java.util.Set;
31+
import java.util.function.BiPredicate;
3132
import java.util.function.Predicate;
3233
import java.util.function.Supplier;
3334
import java.util.stream.Collectors;
@@ -197,6 +198,23 @@ public Stream<JavaPackage> getSubPackagesAnnotatedWith(Class<? extends Annotatio
197198
.map(it -> of(classes, it));
198199
}
199200

201+
/**
202+
* Returns all sub-packages that match the given {@link BiPredicate} for the canidate package and its trailing name
203+
* relative to the current one.
204+
*
205+
* @param filter must not be {@literal null}.
206+
* @return will never be {@literal null}.
207+
* @see #getTrailingName(JavaPackage)
208+
* @since 1.4
209+
*/
210+
public Stream<JavaPackage> getSubPackagesMatching(BiPredicate<JavaPackage, String> filter) {
211+
212+
Assert.notNull(filter, "Filter must not be null!");
213+
214+
return getSubPackages().stream()
215+
.filter(it -> filter.test(it, this.getTrailingName(it)));
216+
}
217+
200218
/**
201219
* Returns all {@link Classes} that match the given {@link DescribedPredicate}.
202220
*

spring-modulith-core/src/main/java/org/springframework/modulith/core/NamedInterface.java

+30-3
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,9 @@ private NamedInterface(String name, Classes classes) {
6969
*
7070
* @param javaPackage must not be {@literal null}.
7171
* @return will never be {@literal null}.
72+
* @since 1.4 (previously package protected)
7273
*/
73-
static List<NamedInterface> of(JavaPackage javaPackage) {
74+
public static List<NamedInterface> of(JavaPackage javaPackage) {
7475

7576
var basePackage = javaPackage.toSingle();
7677
var names = basePackage.findAnnotation(org.springframework.modulith.NamedInterface.class) //
@@ -91,18 +92,44 @@ static List<NamedInterface> of(JavaPackage javaPackage) {
9192
* @param name must not be {@literal null} or empty.
9293
* @param classes must not be {@literal null}.
9394
* @return will never be {@literal null}.
95+
* @since 1.4 (previously package protected)
9496
*/
95-
static NamedInterface of(String name, Classes classes) {
97+
public static NamedInterface of(String name, Classes classes) {
9698
return new NamedInterface(name, classes);
9799
}
98100

101+
/**
102+
* Returns the unnamed interface for the given package, excluding any sub-packages by convention.
103+
*
104+
* @param javaPackage must not be {@literal null}.
105+
* @return will never be {@literal null}.
106+
* @since 1.4
107+
*/
108+
public static NamedInterface unnamed(JavaPackage javaPackage) {
109+
return unnamed(javaPackage, true);
110+
}
111+
112+
/**
113+
* Returns an unnamed interface for the given package applying open module semantics, i.e. including all sub-packages
114+
* of the given one.
115+
*
116+
* @param javaPackage must not be {@literal null}.
117+
* @return will never be {@literal null}.
118+
* @since 1.4
119+
*/
120+
public static NamedInterface open(JavaPackage javaPackage) {
121+
return unnamed(javaPackage, false);
122+
}
123+
99124
/**
100125
* Creates an unnamed {@link NamedInterface} for the given {@link JavaPackage}.
101126
*
102127
* @param javaPackage must not be {@literal null}.
103128
* @return will never be {@literal null}.
104129
*/
105-
static NamedInterface unnamed(JavaPackage javaPackage, boolean flatten) {
130+
private static NamedInterface unnamed(JavaPackage javaPackage, boolean flatten) {
131+
132+
Assert.notNull(javaPackage, "Java package must not be null!");
106133

107134
var basePackageClasses = (flatten ? javaPackage.toSingle() : javaPackage).getExposedClasses();
108135

0 commit comments

Comments
 (0)