spring-projects / spring-projects/spring-modulith

SpringModulithRuntimeAutoConfiguration fails with ClassNotFoundException in Spring Boot executable JAR

Open
#1,556 4 comments 0 reactions 1 assignee View on GitHub

@odrotbohm is already working on this.

Since Feb 11, 2026.

in: observability meta: waiting for feedback
Dominant language
Java
Stars
1.2k
Forks
222
PR merge metrics
No merged PRs in 30d

Description

Description

When running a Spring Boot application packaged as an executable JAR (bootJar), SpringModulithRuntimeAutoConfiguration fails at startup with ClassNotFoundException for @ApplicationModule annotated classes. The same application works correctly when run with bootRun or from an IDE.

Environment

  • Spring Boot: 4.0.1
  • Spring Modulith: 2.0.0
  • Java: 24
  • Kotlin: 2.2.21
  • Build tool: Gradle with bootJar

Dependencies

implementation("org.springframework.modulith:spring-modulith-starter-core")
implementation("org.springframework.modulith:spring-modulith-starter-jdbc")
runtimeOnly("org.springframework.modulith:spring-modulith-actuator")
runtimeOnly("org.springframework.modulith:spring-modulith-observability")

Steps to Reproduce

  1. Create a Spring Modulith application with modules annotated with @ApplicationModule
  2. Include spring-modulith-actuator or spring-modulith-observability as runtime dependencies
  3. Build with ./gradlew bootJar
  4. Run the JAR: java -jar app.jar

Expected Behavior

Application starts successfully.

Actual Behavior

Application fails to start with:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'meterRegistryPostProcessor' defined in class path resource [org/springframework/boot/micrometer/metrics/autoconfigure/MetricsAutoConfiguration.class]: com.tngtech.archunit.base.ArchUnitException$ReflectionException: java.lang.ClassNotFoundException: com.example.myapp.cart.api.ModuleMetadata
    ...
Caused by: com.tngtech.archunit.base.ArchUnitException$ReflectionException: java.lang.ClassNotFoundException: com.example.myapp.cart.api.ModuleMetadata
    at com.tngtech.archunit.core.domain.JavaClassDescriptor$From$AbstractClassDescriptor.resolveClass(JavaClassDescriptor.java:166)
    at com.tngtech.archunit.core.domain.JavaClass$ReflectClassSupplier.get(JavaClass.java:2636)
    ...
    at org.springframework.modulith.core.ApplicationModules.of(ApplicationModules.java:216)
    at org.springframework.modulith.runtime.autoconfigure.SpringModulithRuntimeAutoConfiguration$ApplicationModulesBootstrap.initializeApplicationModules(SpringModulithRuntimeAutoConfiguration.java:215)
    ...
Caused by: java.lang.ClassNotFoundException: com.example.myapp.cart.api.ModuleMetadata
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
    at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Unknown Source)
    at com.tngtech.archunit.core.domain.JavaClassDescriptor$From$AbstractClassDescriptor.classForName(JavaClassDescriptor.java:172)

Root Cause Analysis

The issue is in how ArchUnit resolves classes. SpringModulithRuntimeAutoConfiguration uses ApplicationModules.of() which internally uses ArchUnit to scan the module structure. ArchUnit calls Class.forName() to reflectively load classes.

Spring Boot's executable JAR uses a nested classloader structure where application classes are located in BOOT-INF/classes/. The standard Class.forName() call doesn't work correctly with this nested structure because it uses the system classloader rather than Spring Boot's LaunchedURLClassLoader.

This works with bootRun because the classpath is flat (no nested JARs).

Workarounds

Workaround 1: Exclude auto-configurations via environment variable
SPRING_AUTOCONFIGURE_EXCLUDE=org.springframework.modulith.runtime.autoconfigure.SpringModulithRuntimeAutoConfiguration,org.springframework.modulith.observability.autoconfigure.ModuleObservabilityAutoConfiguration,org.springframework.modulith.actuator.autoconfigure.ApplicationModulesEndpointConfiguration
Workaround 2: Use developmentOnly scope (Gradle)
// Only include in development, not in production JAR
developmentOnly("org.springframework.modulith:spring-modulith-actuator")
developmentOnly("org.springframework.modulith:spring-modulith-observability")

Impact

This affects any Spring Modulith application deployed as a Docker container or any environment using the executable JAR format, which is the standard Spring Boot deployment model.

Related

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.