Skip to content

Commit 7db3897

Browse files
committed
GH-1065 - Avoid the bootstrap of an ApplicationModules instance to execute ApplicationModuleInitializers.
1 parent 65f7bb7 commit 7db3897

File tree

9 files changed

+455
-63
lines changed

9 files changed

+455
-63
lines changed

spring-modulith-runtime/pom.xml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@
3333
<artifactId>jgrapht-core</artifactId>
3434
<version>1.4.0</version>
3535
</dependency>
36+
37+
<dependency>
38+
<groupId>com.jayway.jsonpath</groupId>
39+
<artifactId>json-path</artifactId>
40+
</dependency>
41+
42+
<dependency>
43+
<groupId>net.minidev</groupId>
44+
<artifactId>json-smart</artifactId>
45+
</dependency>
3646

3747
<dependency>
3848
<groupId>org.springframework.boot</groupId>
@@ -60,5 +70,46 @@
6070
</dependency>
6171

6272
</dependencies>
73+
74+
<build>
75+
<plugins>
76+
<plugin>
77+
<groupId>org.apache.maven.plugins</groupId>
78+
<artifactId>maven-shade-plugin</artifactId>
79+
<executions>
80+
<execution>
81+
<phase>package</phase>
82+
<goals>
83+
<goal>shade</goal>
84+
</goals>
85+
<configuration>
86+
87+
<artifactSet>
88+
<includes>
89+
<include>com.jayway.jsonpath:*</include>
90+
<include>net.minidev:*</include>
91+
</includes>
92+
</artifactSet>
93+
94+
<relocations>
95+
<relocation>
96+
<pattern>com.jayway.jsonpath</pattern>
97+
<shadedPattern>org.springframework.modulith.runtime.jsonpath</shadedPattern>
98+
</relocation>
99+
<relocation>
100+
<pattern>net.minidev</pattern>
101+
<shadedPattern>org.springframework.modulith.runtime.minidev</shadedPattern>
102+
</relocation>
103+
</relocations>
104+
105+
<dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation>
106+
<minimizeJar>true</minimizeJar>
107+
108+
</configuration>
109+
</execution>
110+
</executions>
111+
</plugin>
112+
</plugins>
113+
</build>
63114

64115
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.modulith.runtime.autoconfigure;
17+
18+
import java.util.stream.Stream;
19+
20+
import org.springframework.modulith.ApplicationModuleInitializer;
21+
22+
/**
23+
* @author Oliver Drotbohm
24+
*/
25+
interface ApplicationModuleInitializerInvoker {
26+
27+
void invokeInitializers(Stream<ApplicationModuleInitializer> initializers);
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.modulith.runtime.autoconfigure;
17+
18+
import java.util.function.Supplier;
19+
import java.util.stream.Stream;
20+
21+
import org.springframework.modulith.ApplicationModuleInitializer;
22+
import org.springframework.modulith.core.ApplicationModules;
23+
24+
/**
25+
* @author Oliver Drotbohm
26+
*/
27+
class DefaultApplicationModuleInitializerInvoker implements ApplicationModuleInitializerInvoker {
28+
29+
private final Supplier<ApplicationModules> modules;
30+
31+
/**
32+
* @param modules
33+
*/
34+
DefaultApplicationModuleInitializerInvoker(Supplier<ApplicationModules> modules) {
35+
this.modules = modules;
36+
}
37+
38+
/*
39+
*
40+
* (non-Javadoc)
41+
* @see org.springframework.modulith.runtime.autoconfigure.ApplicationModuleInitializerInvoker#invokeInitializers(java.util.stream.Stream)
42+
*/
43+
@Override
44+
public void invokeInitializers(Stream<ApplicationModuleInitializer> initializers) {
45+
46+
var modules = this.modules.get();
47+
48+
initializers
49+
.sorted(modules.getComparator()) //
50+
.map(it -> LoggingApplicationModuleInitializerAdapter.of(it, modules))
51+
.forEach(ApplicationModuleInitializer::initialize);
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.modulith.runtime.autoconfigure;
17+
18+
import java.util.stream.Collectors;
19+
import java.util.stream.Stream;
20+
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
import org.springframework.aop.support.AopUtils;
24+
import org.springframework.modulith.ApplicationModuleInitializer;
25+
import org.springframework.modulith.core.ApplicationModules;
26+
import org.springframework.modulith.core.FormattableType;
27+
import org.springframework.util.Assert;
28+
29+
class LoggingApplicationModuleInitializerAdapter implements ApplicationModuleInitializer {
30+
31+
private static final Logger LOGGER = LoggerFactory.getLogger(LoggingApplicationModuleInitializerAdapter.class);
32+
33+
private final ApplicationModuleInitializer delegate;
34+
private final String label;
35+
36+
/**
37+
* Creates a new {@link LoggingApplicationModuleInitializerAdapter} for the given {@link ApplicationModuleInitializer}
38+
* and identifier.
39+
*
40+
* @param delegate must not be {@literal null}.
41+
* @param label must not be {@literal null}.
42+
*/
43+
private LoggingApplicationModuleInitializerAdapter(ApplicationModuleInitializer delegate, String label) {
44+
45+
Assert.notNull(delegate, "ApplicationModuleInitializer must not be null!");
46+
Assert.hasText(label, "Label must not be null or empty!");
47+
48+
this.delegate = delegate;
49+
this.label = label;
50+
}
51+
52+
public static ApplicationModuleInitializer of(ApplicationModuleInitializer initializer,
53+
ApplicationModules modules) {
54+
55+
if (!LoggerFactory.getLogger(initializer.getClass()).isDebugEnabled()) {
56+
return initializer;
57+
}
58+
59+
var listenerType = AopUtils.getTargetClass(initializer);
60+
var formattable = FormattableType.of(listenerType);
61+
62+
var formattedListenerType = modules.getModuleByType(listenerType)
63+
.map(formattable::getAbbreviatedFullName)
64+
.orElseGet(formattable::getAbbreviatedFullName);
65+
66+
return new LoggingApplicationModuleInitializerAdapter(initializer, formattedListenerType);
67+
}
68+
69+
public static ApplicationModuleInitializer of(ApplicationModuleInitializer initializer) {
70+
71+
if (!LoggerFactory.getLogger(initializer.getClass()).isDebugEnabled()) {
72+
return initializer;
73+
}
74+
75+
var listenerType = AopUtils.getTargetClass(initializer);
76+
var abbreviatedPackage = Stream.of(listenerType.getPackageName().split("\\."))
77+
.map(it -> it.substring(0, 1))
78+
.collect(Collectors.joining("."));
79+
80+
return new LoggingApplicationModuleInitializerAdapter(initializer,
81+
abbreviatedPackage + "." + listenerType.getSimpleName());
82+
}
83+
84+
/*
85+
* (non-Javadoc)
86+
* @see org.springframework.modulith.ApplicationModuleInitializer#initialize()
87+
*/
88+
@Override
89+
public void initialize() {
90+
91+
LOGGER.debug("Initializing {}.", label);
92+
93+
delegate.initialize();
94+
95+
LOGGER.debug("Initializing {} done.", label);
96+
}
97+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.modulith.runtime.autoconfigure;
17+
18+
import java.io.IOException;
19+
import java.io.UncheckedIOException;
20+
import java.util.List;
21+
import java.util.Optional;
22+
import java.util.function.Function;
23+
import java.util.stream.Collectors;
24+
import java.util.stream.Stream;
25+
26+
import org.springframework.core.io.Resource;
27+
import org.springframework.modulith.ApplicationModuleInitializer;
28+
import org.springframework.util.Assert;
29+
30+
import com.jayway.jsonpath.JsonPath;
31+
32+
class PrecomputedApplicationModuleInitializerInvoker implements ApplicationModuleInitializerInvoker {
33+
34+
private List<String> plan;
35+
36+
public PrecomputedApplicationModuleInitializerInvoker(Resource metadata) {
37+
38+
Assert.isTrue(metadata.exists(), () -> "Resource %s does not exist!".formatted(metadata.getDescription()));
39+
40+
try (var stream = metadata.getInputStream()) {
41+
42+
this.plan = JsonPath.parse(stream).<List<String>> read("$..initializers[*]");
43+
44+
} catch (IOException e) {
45+
throw new UncheckedIOException(e);
46+
}
47+
}
48+
49+
/*
50+
* (non-Javadoc)
51+
* @see org.springframework.modulith.runtime.autoconfigure.ApplicationModuleInitializerInvoker#invokeInitializers(java.util.stream.Stream)
52+
*/
53+
@Override
54+
public void invokeInitializers(Stream<ApplicationModuleInitializer> initializers) {
55+
56+
var map = initializers
57+
.collect(Collectors.toMap(it -> it.getClass().getName(), Function.identity()));
58+
59+
plan.stream()
60+
.map(map::get)
61+
.map(Optional::ofNullable)
62+
.flatMap(Optional::stream)
63+
.map(LoggingApplicationModuleInitializerAdapter::of)
64+
.forEach(ApplicationModuleInitializer::initialize);
65+
}
66+
}

0 commit comments

Comments
 (0)