diff --git a/spring-modulith-runtime/pom.xml b/spring-modulith-runtime/pom.xml
index a7dc20ef..cec44d4c 100644
--- a/spring-modulith-runtime/pom.xml
+++ b/spring-modulith-runtime/pom.xml
@@ -33,6 +33,16 @@
jgrapht-core
1.4.0
+
+
+ com.jayway.jsonpath
+ json-path
+
+
+
+ net.minidev
+ json-smart
+
org.springframework.boot
@@ -60,5 +70,46 @@
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+
+
+ package
+
+ shade
+
+
+
+
+
+ com.jayway.jsonpath:*
+ net.minidev:*
+
+
+
+
+
+ com.jayway.jsonpath
+ org.springframework.modulith.runtime.jsonpath
+
+
+ net.minidev
+ org.springframework.modulith.runtime.minidev
+
+
+
+ ${project.build.directory}/dependency-reduced-pom.xml
+ true
+
+
+
+
+
+
+
diff --git a/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/ApplicationModuleInitializerInvoker.java b/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/ApplicationModuleInitializerInvoker.java
new file mode 100644
index 00000000..a4c89acf
--- /dev/null
+++ b/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/ApplicationModuleInitializerInvoker.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2025 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.runtime.autoconfigure;
+
+import java.util.stream.Stream;
+
+import org.springframework.modulith.ApplicationModuleInitializer;
+
+/**
+ * @author Oliver Drotbohm
+ */
+interface ApplicationModuleInitializerInvoker {
+
+ void invokeInitializers(Stream initializers);
+}
diff --git a/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/DefaultApplicationModuleInitializerInvoker.java b/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/DefaultApplicationModuleInitializerInvoker.java
new file mode 100644
index 00000000..e4fbfb51
--- /dev/null
+++ b/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/DefaultApplicationModuleInitializerInvoker.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2025 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.runtime.autoconfigure;
+
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import org.springframework.modulith.ApplicationModuleInitializer;
+import org.springframework.modulith.core.ApplicationModules;
+
+/**
+ * @author Oliver Drotbohm
+ */
+class DefaultApplicationModuleInitializerInvoker implements ApplicationModuleInitializerInvoker {
+
+ private final Supplier modules;
+
+ /**
+ * @param modules
+ */
+ DefaultApplicationModuleInitializerInvoker(Supplier modules) {
+ this.modules = modules;
+ }
+
+ /*
+ *
+ * (non-Javadoc)
+ * @see org.springframework.modulith.runtime.autoconfigure.ApplicationModuleInitializerInvoker#invokeInitializers(java.util.stream.Stream)
+ */
+ @Override
+ public void invokeInitializers(Stream initializers) {
+
+ var modules = this.modules.get();
+
+ initializers
+ .sorted(modules.getComparator()) //
+ .map(it -> LoggingApplicationModuleInitializerAdapter.of(it, modules))
+ .forEach(ApplicationModuleInitializer::initialize);
+ }
+}
diff --git a/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/LoggingApplicationModuleInitializerAdapter.java b/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/LoggingApplicationModuleInitializerAdapter.java
new file mode 100644
index 00000000..6bce4f5c
--- /dev/null
+++ b/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/LoggingApplicationModuleInitializerAdapter.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 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.runtime.autoconfigure;
+
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aop.support.AopUtils;
+import org.springframework.modulith.ApplicationModuleInitializer;
+import org.springframework.modulith.core.ApplicationModules;
+import org.springframework.modulith.core.FormattableType;
+import org.springframework.util.Assert;
+
+class LoggingApplicationModuleInitializerAdapter implements ApplicationModuleInitializer {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(LoggingApplicationModuleInitializerAdapter.class);
+
+ private final ApplicationModuleInitializer delegate;
+ private final String label;
+
+ /**
+ * Creates a new {@link LoggingApplicationModuleInitializerAdapter} for the given {@link ApplicationModuleInitializer}
+ * and identifier.
+ *
+ * @param delegate must not be {@literal null}.
+ * @param label must not be {@literal null}.
+ */
+ private LoggingApplicationModuleInitializerAdapter(ApplicationModuleInitializer delegate, String label) {
+
+ Assert.notNull(delegate, "ApplicationModuleInitializer must not be null!");
+ Assert.hasText(label, "Label must not be null or empty!");
+
+ this.delegate = delegate;
+ this.label = label;
+ }
+
+ public static ApplicationModuleInitializer of(ApplicationModuleInitializer initializer,
+ ApplicationModules modules) {
+
+ if (!LoggerFactory.getLogger(initializer.getClass()).isDebugEnabled()) {
+ return initializer;
+ }
+
+ var listenerType = AopUtils.getTargetClass(initializer);
+ var formattable = FormattableType.of(listenerType);
+
+ var formattedListenerType = modules.getModuleByType(listenerType)
+ .map(formattable::getAbbreviatedFullName)
+ .orElseGet(formattable::getAbbreviatedFullName);
+
+ return new LoggingApplicationModuleInitializerAdapter(initializer, formattedListenerType);
+ }
+
+ public static ApplicationModuleInitializer of(ApplicationModuleInitializer initializer) {
+
+ if (!LoggerFactory.getLogger(initializer.getClass()).isDebugEnabled()) {
+ return initializer;
+ }
+
+ var listenerType = AopUtils.getTargetClass(initializer);
+ var abbreviatedPackage = Stream.of(listenerType.getPackageName().split("\\."))
+ .map(it -> it.substring(0, 1))
+ .collect(Collectors.joining("."));
+
+ return new LoggingApplicationModuleInitializerAdapter(initializer,
+ abbreviatedPackage + "." + listenerType.getSimpleName());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.modulith.ApplicationModuleInitializer#initialize()
+ */
+ @Override
+ public void initialize() {
+
+ LOGGER.debug("Initializing {}.", label);
+
+ delegate.initialize();
+
+ LOGGER.debug("Initializing {} done.", label);
+ }
+}
diff --git a/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/PrecomputedApplicationModuleInitializerInvoker.java b/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/PrecomputedApplicationModuleInitializerInvoker.java
new file mode 100644
index 00000000..7f42c095
--- /dev/null
+++ b/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/PrecomputedApplicationModuleInitializerInvoker.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2025 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.runtime.autoconfigure;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.springframework.core.io.Resource;
+import org.springframework.modulith.ApplicationModuleInitializer;
+import org.springframework.util.Assert;
+
+import com.jayway.jsonpath.JsonPath;
+
+class PrecomputedApplicationModuleInitializerInvoker implements ApplicationModuleInitializerInvoker {
+
+ private List plan;
+
+ public PrecomputedApplicationModuleInitializerInvoker(Resource metadata) {
+
+ Assert.isTrue(metadata.exists(), () -> "Resource %s does not exist!".formatted(metadata.getDescription()));
+
+ try (var stream = metadata.getInputStream()) {
+
+ this.plan = JsonPath.parse(stream).> read("$..initializers[*]");
+
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.modulith.runtime.autoconfigure.ApplicationModuleInitializerInvoker#invokeInitializers(java.util.stream.Stream)
+ */
+ @Override
+ public void invokeInitializers(Stream initializers) {
+
+ var map = initializers
+ .collect(Collectors.toMap(it -> it.getClass().getName(), Function.identity()));
+
+ plan.stream()
+ .map(map::get)
+ .map(Optional::ofNullable)
+ .flatMap(Optional::stream)
+ .map(LoggingApplicationModuleInitializerAdapter::of)
+ .forEach(ApplicationModuleInitializer::initialize);
+ }
+}
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 d757ff60..e6c378bc 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
@@ -17,8 +17,8 @@ package org.springframework.modulith.runtime.autoconfigure;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -28,7 +28,9 @@ 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.context.annotation.Lazy;
import org.springframework.context.annotation.Role;
+import org.springframework.core.io.Resource;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
@@ -36,10 +38,9 @@ import org.springframework.modulith.ApplicationModuleInitializer;
import org.springframework.modulith.core.ApplicationModule;
import org.springframework.modulith.core.ApplicationModules;
import org.springframework.modulith.core.ApplicationModulesFactory;
-import org.springframework.modulith.core.FormattableType;
+import org.springframework.modulith.core.util.ApplicationModulesExporter;
import org.springframework.modulith.runtime.ApplicationModulesRuntime;
import org.springframework.modulith.runtime.ApplicationRuntime;
-import org.springframework.util.Assert;
import org.springframework.util.function.ThrowingSupplier;
/**
@@ -51,10 +52,10 @@ import org.springframework.util.function.ThrowingSupplier;
@AutoConfiguration
class SpringModulithRuntimeAutoConfiguration {
- private static final Logger LOGGER = LoggerFactory.getLogger(SpringModulithRuntimeAutoConfiguration.class);
private static final AsyncTaskExecutor EXECUTOR = new SimpleAsyncTaskExecutor();
@Bean
+ @Lazy
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ConditionalOnMissingBean(ApplicationRuntime.class)
static ApplicationRuntime modulithsApplicationRuntime(ApplicationContext context) {
@@ -62,6 +63,7 @@ class SpringModulithRuntimeAutoConfiguration {
}
@Bean
+ @Lazy
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ConditionalOnMissingBean
static ApplicationModulesRuntime modulesRuntime(ApplicationRuntime runtime) {
@@ -77,64 +79,20 @@ class SpringModulithRuntimeAutoConfiguration {
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ConditionalOnBean(ApplicationModuleInitializer.class)
static ApplicationListener applicationModuleInitializingListener(
- ObjectProvider runtime,
- ObjectProvider initializers) {
-
- return event -> {
-
- var modules = runtime.getObject().get();
-
- initializers.stream() //
- .sorted(modules.getComparator()) //
- .map(it -> LOGGER.isDebugEnabled() ? new LoggingApplicationModuleInitializerAdapter(it, modules) : it)
- .forEach(ApplicationModuleInitializer::initialize);
- };
+ ApplicationModuleInitializerInvoker invoker, ObjectProvider initializers) {
+ return __ -> invoker.invokeInitializers(initializers.stream());
}
- private static class LoggingApplicationModuleInitializerAdapter implements ApplicationModuleInitializer {
+ @Bean
+ @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+ @ConditionalOnBean(ApplicationModuleInitializer.class)
+ static ApplicationModuleInitializerInvoker applicationModuleInitializerInvoker(
+ @Value("classpath:" + ApplicationModulesExporter.DEFAULT_LOCATION) Resource metadata,
+ ObjectProvider runtime) {
- private static final Logger LOGGER = LoggerFactory.getLogger(LoggingApplicationModuleInitializerAdapter.class);
-
- private final ApplicationModuleInitializer delegate;
- private final ApplicationModules modules;
-
- /**
- * Creates a new {@link LoggingApplicationModuleInitializerAdapter} for the given
- * {@link ApplicationModuleInitializer} and {@link ApplicationModule}.
- *
- * @param delegate must not be {@literal null}.
- * @param modules must not be {@literal null}.
- */
- public LoggingApplicationModuleInitializerAdapter(ApplicationModuleInitializer delegate,
- ApplicationModules modules) {
-
- Assert.notNull(delegate, "ApplicationModuleInitializer must not be null!");
- Assert.notNull(modules, "ApplicationModules must not be null!");
-
- this.delegate = delegate;
- this.modules = modules;
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.modulith.ApplicationModuleInitializer#initialize()
- */
- @Override
- public void initialize() {
-
- var listenerType = AopUtils.getTargetClass(delegate);
- var formattable = FormattableType.of(listenerType);
-
- var formattedListenerType = modules.getModuleByType(listenerType)
- .map(formattable::getAbbreviatedFullName)
- .orElseGet(formattable::getAbbreviatedFullName);
-
- LOGGER.debug("Initializing {}.", formattedListenerType);
-
- delegate.initialize();
-
- LOGGER.debug("Initializing {} done.", formattedListenerType);
- }
+ return metadata.exists()
+ ? new PrecomputedApplicationModuleInitializerInvoker(metadata)
+ : new DefaultApplicationModuleInitializerInvoker(runtime.getIfAvailable());
}
private static class ApplicationModulesBootstrap {
@@ -165,7 +123,7 @@ class SpringModulithRuntimeAutoConfiguration {
LOGGER.debug("Detected {} application modules: {}", //
numberOfModules, //
- result.stream().map(ApplicationModule::getName).toList());
+ result.stream().map(ApplicationModule::getIdentifier).toList());
}
return result;
diff --git a/spring-modulith-runtime/src/test/java/org/springframework/modulith/runtime/autoconfigure/PrecomputedApplicationModuleInitializerInvokerUnitTests.java b/spring-modulith-runtime/src/test/java/org/springframework/modulith/runtime/autoconfigure/PrecomputedApplicationModuleInitializerInvokerUnitTests.java
new file mode 100644
index 00000000..00f32284
--- /dev/null
+++ b/spring-modulith-runtime/src/test/java/org/springframework/modulith/runtime/autoconfigure/PrecomputedApplicationModuleInitializerInvokerUnitTests.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2025 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.runtime.autoconfigure;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.modulith.ApplicationModuleInitializer;
+
+/**
+ * @author Oliver Drotbohm
+ */
+class PrecomputedApplicationModuleInitializerInvokerUnitTests {
+
+ @Test
+ void ordersInitializersBasedOnPrecomputedMetadata() throws IOException {
+
+ var resource = new ClassPathResource("application-modules.json");
+ var invoker = new PrecomputedApplicationModuleInitializerInvoker(resource);
+
+ var second = new SecondApplicationModuleInitializer();
+ var first = new FirstApplicationModuleInitializer();
+
+ invoker.invokeInitializers(Stream.of(second, first));
+
+ assertThat(first.invoked).isBefore(second.invoked);
+ }
+
+ static class FirstApplicationModuleInitializer implements ApplicationModuleInitializer {
+
+ LocalDateTime invoked;
+
+ @Override
+ public void initialize() {
+ invoked = LocalDateTime.now();
+ }
+ }
+
+ static class SecondApplicationModuleInitializer implements ApplicationModuleInitializer {
+
+ LocalDateTime invoked;
+
+ @Override
+ public void initialize() {
+ invoked = LocalDateTime.now();
+ }
+ }
+}
diff --git a/spring-modulith-runtime/src/test/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfigurationIntegrationTests.java b/spring-modulith-runtime/src/test/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfigurationIntegrationTests.java
index 405542cb..8cbd1414 100644
--- a/spring-modulith-runtime/src/test/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfigurationIntegrationTests.java
+++ b/spring-modulith-runtime/src/test/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfigurationIntegrationTests.java
@@ -16,11 +16,22 @@
package org.springframework.modulith.runtime.autoconfigure;
import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.util.HashMap;
+import java.util.Map;
import org.junit.jupiter.api.Test;
+import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeanInstantiationException;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.ListableBeanFactory;
+import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.modulith.ApplicationModuleInitializer;
@@ -70,15 +81,69 @@ class SpringModulithRuntimeAutoConfigurationIntegrationTests {
@Test // GH-375
void registersInitializingListenerIfInitializersPresent() {
+ var tracker = new InitializationTracker();
+
var beanName = "applicationModuleInitializingListener";
var runner = new ApplicationContextRunner()
- .withConfiguration(AutoConfigurations.of(SpringModulithRuntimeAutoConfiguration.class));
+ .withConfiguration(AutoConfigurations.of(SpringModulithRuntimeAutoConfiguration.class))
+ .withBean(InitializationTracker.class, () -> tracker);
// No initializer -> no listener
- runner.run(context -> assertThat(context).doesNotHaveBean(beanName));
+ runner.run(context -> {
+
+ assertThat(context).doesNotHaveBean(beanName);
+ tracker.assertBeanNotInitialized(ApplicationModulesRuntime.class);
+ });
// Initializer -> listener
runner.withBean(ApplicationModuleInitializer.class, () -> () -> {})
- .run(context -> assertThat(context).hasBean(beanName));
+ .withBean(DummyApplication.class)
+ .run(context -> {
+
+ context.publishEvent(mock(ApplicationStartedEvent.class));
+ assertThat(context).hasBean(beanName);
+ });
}
+
+ static class InitializationTracker implements BeanPostProcessor, BeanFactoryAware {
+
+ private final Map> initializedBeans = new HashMap<>();
+ private ListableBeanFactory beanFactory;
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory)
+ */
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ this.beanFactory = (ListableBeanFactory) beanFactory;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
+ */
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+
+ initializedBeans.put(beanName, AopUtils.getTargetClass(bean));
+
+ return bean;
+ }
+
+ public void assertBeanInitialized(Class> type) {
+ assertThat(initializedBeans).containsValue(type);
+ }
+
+ public void assertBeanNotInitialized(Class> type) {
+
+ var names = beanFactory.getBeanNamesForType(type);
+
+ assertThat(names).isNotEmpty();
+ assertThat(initializedBeans).doesNotContainKeys(names);
+ }
+ }
+
+ @SpringBootApplication
+ static class DummyApplication {}
}
diff --git a/spring-modulith-runtime/src/test/resources/application-modules.json b/spring-modulith-runtime/src/test/resources/application-modules.json
new file mode 100644
index 00000000..2d00a237
--- /dev/null
+++ b/spring-modulith-runtime/src/test/resources/application-modules.json
@@ -0,0 +1,8 @@
+{
+ "a" : {
+ "initializers" : [ "org.springframework.modulith.runtime.autoconfigure.PrecomputedApplicationModuleInitializerInvokerUnitTests$FirstApplicationModuleInitializer" ]
+ },
+ "b" : {
+ "initializers" : [ "org.springframework.modulith.runtime.autoconfigure.PrecomputedApplicationModuleInitializerInvokerUnitTests$SecondApplicationModuleInitializer" ]
+ }
+}