Skip to content

SpringModulithRuntimeAutoConfiguration fails with ClassNotFoundException in Spring Boot executable JAR #1556

@refaatcrafts

Description

@refaatcrafts

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions