Introduction

When building a fat JAR (uber JAR) with the Maven Shade Plugin, all project dependencies must be bundled into the single JAR file. If a dependency is missing from the shade configuration, excluded by a filter, or a resource transformer is misconfigured, the application throws ClassNotFoundException at runtime even though mvn compile succeeded. This is a common issue in Spring Boot and other framework applications.

Symptoms

  • java.lang.ClassNotFoundException: com.fasterxml.jackson.databind.ObjectMapper
  • java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
  • Application works in IDE but fails as packaged JAR
  • mvn compile succeeds but java -jar target/app.jar fails
  • Error occurs only for specific classes, not all dependencies
bash
$ java -jar target/myapp-1.0.0.jar
Exception in thread "main" java.lang.NoClassDefFoundError: com/fasterxml/jackson/databind/ObjectMapper
    at com.example.App.main(App.java:15)
Caused by: java.lang.ClassNotFoundException: com.fasterxml.jackson.databind.ObjectMapper
    at java.net.URLClassLoader.findClass(URLClassLoader.java:445)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:592)

Common Causes

  • Shade plugin not configured in pom.xml
  • <filters> in shade config excluding required classes
  • minimizeJar removing classes that are actually needed
  • Resource transformers not merging service files (META-INF/services)
  • Scope provided dependencies not included in fat JAR

Step-by-Step Fix

  1. 1.Configure Maven Shade Plugin properly:
  2. 2.```xml
  3. 3.<build>
  4. 4.<plugins>
  5. 5.<plugin>
  6. 6.<groupId>org.apache.maven.plugins</groupId>
  7. 7.<artifactId>maven-shade-plugin</artifactId>
  8. 8.<version>3.5.1</version>
  9. 9.<executions>
  10. 10.<execution>
  11. 11.<phase>package</phase>
  12. 12.<goals><goal>shade</goal></goals>
  13. 13.<configuration>
  14. 14.<transformers>
  15. 15.<!-- Merge META-INF/services files -->
  16. 16.<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
  17. 17.<!-- Set main class in MANIFEST.MF -->
  18. 18.<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
  19. 19.<mainClass>com.example.App</mainClass>
  20. 20.</transformer>
  21. 21.<!-- Merge application.properties -->
  22. 22.<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
  23. 23.<resource>META-INF/spring.handlers</resource>
  24. 24.</transformer>
  25. 25.</transformers>
  26. 26.</configuration>
  27. 27.</execution>
  28. 28.</executions>
  29. 29.</plugin>
  30. 30.</plugins>
  31. 31.</build>
  32. 32.`
  33. 33.Remove over-aggressive jar filtering:
  34. 34.```xml
  35. 35.<!-- WRONG - filters out everything except specific packages -->
  36. 36.<filters>
  37. 37.<filter>
  38. 38.<artifact>*:*</artifact>
  39. 39.<excludes>
  40. 40.<exclude>META-INF/*.SF</exclude>
  41. 41.<exclude>META-INF/*.DSA</exclude>
  42. 42.<exclude>META-INF/*.RSA</exclude>
  43. 43.<!-- Too aggressive: excludes actual code -->
  44. 44.<exclude>**/*.class</exclude>
  45. 45.</excludes>
  46. 46.</filter>
  47. 47.</filters>

<!-- CORRECT - only exclude signature files --> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> </excludes> </filter> </filters> ```

  1. 1.Verify JAR contents:
  2. 2.```bash
  3. 3.# Check if class is in the JAR
  4. 4.jar tf target/myapp-1.0.0.jar | grep ObjectMapper
  5. 5.# Should show: com/fasterxml/jackson/databind/ObjectMapper.class

# If missing, check dependency scope in pom.xml mvn dependency:tree | grep jackson

# Check if dependency has 'provided' scope (not included in fat JAR) mvn dependency:tree -Dincludes=com.fasterxml.jackson.core ```

  1. 1.Fix dependency scope:
  2. 2.```xml
  3. 3.<!-- WRONG - 'provided' means not included in fat JAR -->
  4. 4.<dependency>
  5. 5.<groupId>com.fasterxml.jackson.core</groupId>
  6. 6.<artifactId>jackson-databind</artifactId>
  7. 7.<version>2.16.0</version>
  8. 8.<scope>provided</scope>
  9. 9.</dependency>

<!-- CORRECT - default 'compile' scope includes in fat JAR --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.16.0</version> </dependency> ```

Prevention

  • Run jar tf target/*.jar | grep <ClassName> after build to verify
  • Use mvn dependency:tree to check dependency scopes
  • Add a smoke test that runs java -jar as part of CI
  • Consider Spring Boot Maven Plugin for Spring applications (handles shading automatically)
  • Use maven-dependency-plugin:analyze to detect unused/undeclared dependencies
  • Never use minimizeJar without thorough integration testing