diff --git a/spring-modulith-api/src/main/java/org/springframework/modulith/ApplicationModuleInitializer.java b/spring-modulith-api/src/main/java/org/springframework/modulith/ApplicationModuleInitializer.java
new file mode 100644
index 00000000..5e5e78a3
--- /dev/null
+++ b/spring-modulith-api/src/main/java/org/springframework/modulith/ApplicationModuleInitializer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.modulith;
+
+import org.springframework.boot.context.event.ApplicationStartedEvent;
+
+/**
+ * An interface to be implemented by Spring components that are supposed to be initialized on
+ * {@link ApplicationStartedEvent}. They're {@link ApplicationModule} specific as they're executed in the order
+ * determined by the dependencies of a module. In other words, {@link ApplicationModuleInitializer} of fundamental
+ * application modules are executed first, followed by the ones that depend on it.
+ *
+ * @author Oliver Drotbohm
+ */
+public interface ApplicationModuleInitializer {
+
+ /**
+ * Run business logic that's needed to initialize the {@link ApplicationModule}.
+ */
+ void initialize();
+}
diff --git a/spring-modulith-runtime/pom.xml b/spring-modulith-runtime/pom.xml
index f9630e24..5365eed8 100644
--- a/spring-modulith-runtime/pom.xml
+++ b/spring-modulith-runtime/pom.xml
@@ -22,6 +22,12 @@
${project.version}
+
+ org.jgrapht
+ jgrapht-core
+ 1.4.0
+
+
org.springframework.boot
spring-boot-autoconfigure
diff --git a/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfiguration.java b/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfiguration.java
index c34254b9..ad3d2ebf 100644
--- a/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfiguration.java
+++ b/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfiguration.java
@@ -15,20 +15,27 @@
*/
package org.springframework.modulith.runtime.autoconfigure;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.function.Supplier;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
+import org.springframework.modulith.ApplicationModuleInitializer;
import org.springframework.modulith.model.ApplicationModule;
import org.springframework.modulith.model.ApplicationModules;
+import org.springframework.modulith.model.FormatableType;
import org.springframework.modulith.runtime.ApplicationModulesRuntime;
import org.springframework.modulith.runtime.ApplicationRuntime;
+import org.springframework.util.ClassUtils;
/**
* Auto-configuration to register a {@link SpringBootApplicationRuntime} and {@link ApplicationModulesRuntime} as Spring
@@ -57,6 +64,50 @@ class SpringModulithRuntimeAutoConfiguration {
return new ApplicationModulesRuntime(toSupplier(modules), runtime);
}
+ @Bean
+ ApplicationListener applicationModuleInitialzingListener(ApplicationModulesRuntime runtime,
+ List initializers) {
+
+ return event -> {
+
+ var modules = runtime.get();
+
+ initializers.stream() //
+ .sorted(modules.getComparator()) //
+ .map(it -> LOG.isDebugEnabled() ? new LoggingApplicationModuleInitializerAdapter(it, modules) : it)
+ .forEach(ApplicationModuleInitializer::initialize);
+ };
+ }
+
+ @Slf4j
+ @RequiredArgsConstructor
+ private static class LoggingApplicationModuleInitializerAdapter implements ApplicationModuleInitializer {
+
+ private final ApplicationModuleInitializer delegate;
+ private final ApplicationModules modules;
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.modulith.ApplicationModuleInitializer#initialize()
+ */
+ @Override
+ public void initialize() {
+
+ var listenerType = ClassUtils.getUserClass(delegate);
+ var formattable = FormatableType.of(listenerType);
+
+ var formattedListenerType = modules.getModuleByType(listenerType)
+ .map(formattable::getAbbreviatedFullName)
+ .orElseGet(formattable::getAbbreviatedFullName);
+
+ LOG.debug("Initializing {}.", formattedListenerType);
+
+ delegate.initialize();
+
+ LOG.debug("{} done.", formattedListenerType);
+ }
+ }
+
private static ApplicationModules initializeApplicationModules(Class> applicationMainClass) {
LOG.debug("Obtaining Spring Modulith application modules…");