Skip to content

Commit 397fad0

Browse files
committed
GH-1487 - Fall back to Spring-based task executor for ApplicationModulesBootstrap.
We are now using SimpleAsyncTaskExecutor as a fallback in case ApplicationModulesBootstrap is invoked without an Executor instance in the first place. That makes sure that the ClassLoader is properly propagated to the new Thread created and user classes are fully loadable.
1 parent 85b36b7 commit 397fad0

File tree

1 file changed

+24
-1
lines changed

1 file changed

+24
-1
lines changed

spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfiguration.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,15 @@
4242
import org.springframework.boot.context.event.ApplicationStartedEvent;
4343
import org.springframework.boot.context.properties.EnableConfigurationProperties;
4444
import org.springframework.boot.flyway.autoconfigure.FlywayMigrationStrategy;
45+
import org.springframework.boot.system.JavaVersion;
4546
import org.springframework.context.ApplicationContext;
4647
import org.springframework.context.ApplicationListener;
4748
import org.springframework.context.annotation.Bean;
4849
import org.springframework.context.annotation.Lazy;
4950
import org.springframework.context.annotation.Role;
5051
import org.springframework.core.io.Resource;
5152
import org.springframework.core.io.support.SpringFactoriesLoader;
53+
import org.springframework.core.task.SimpleAsyncTaskExecutor;
5254
import org.springframework.modulith.ApplicationModuleInitializer;
5355
import org.springframework.modulith.core.ApplicationModule;
5456
import org.springframework.modulith.core.ApplicationModuleIdentifier;
@@ -200,14 +202,35 @@ static class ApplicationModulesBootstrap {
200202
Supplier<ApplicationModules> supplier = () -> initializeApplicationModules(applicationMainClass);
201203

202204
this.modules = executor == null
203-
? CompletableFuture.supplyAsync(supplier)
205+
? withFallbackExecutor(supplier)
204206
: CompletableFuture.supplyAsync(supplier, executor);
207+
205208
}
206209

207210
CompletableFuture<ApplicationModules> getApplicationModules() {
208211
return modules;
209212
}
210213

214+
/**
215+
* We didn't get an {@link Executor} instance handed in, so asynchronously execute the supplier through a Spring
216+
* {@link SimpleAsyncTaskExecutor} to make sure we see the right {@link ClassLoader} during the execution.
217+
*
218+
* @param <T>
219+
* @param supplier
220+
* @return
221+
*/
222+
private static <T> CompletableFuture<T> withFallbackExecutor(Supplier<T> supplier) {
223+
224+
var fallback = new SimpleAsyncTaskExecutor();
225+
226+
if (JavaVersion.getJavaVersion().isEqualOrNewerThan(JavaVersion.TWENTY_ONE)) {
227+
fallback.setVirtualThreads(true);
228+
}
229+
230+
return fallback.submitCompletable(supplier::get)
231+
.whenComplete((m, t) -> fallback.close());
232+
}
233+
211234
static ApplicationModules initializeApplicationModules(Class<?> applicationMainClass) {
212235

213236
LOGGER.debug("Obtaining Spring Modulith application modules…");

0 commit comments

Comments
 (0)