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.ObjectMapperjava.lang.NoClassDefFoundError: org/slf4j/LoggerFactory- Application works in IDE but fails as packaged JAR
mvn compilesucceeds butjava -jar target/app.jarfails- Error occurs only for specific classes, not all dependencies
$ 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 classesminimizeJarremoving classes that are actually needed- Resource transformers not merging service files (META-INF/services)
- Scope
provideddependencies not included in fat JAR
Step-by-Step Fix
- 1.Configure Maven Shade Plugin properly:
- 2.```xml
- 3.<build>
- 4.<plugins>
- 5.<plugin>
- 6.<groupId>org.apache.maven.plugins</groupId>
- 7.<artifactId>maven-shade-plugin</artifactId>
- 8.<version>3.5.1</version>
- 9.<executions>
- 10.<execution>
- 11.<phase>package</phase>
- 12.<goals><goal>shade</goal></goals>
- 13.<configuration>
- 14.<transformers>
- 15.<!-- Merge META-INF/services files -->
- 16.<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
- 17.<!-- Set main class in MANIFEST.MF -->
- 18.<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
- 19.<mainClass>com.example.App</mainClass>
- 20.</transformer>
- 21.<!-- Merge application.properties -->
- 22.<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
- 23.<resource>META-INF/spring.handlers</resource>
- 24.</transformer>
- 25.</transformers>
- 26.</configuration>
- 27.</execution>
- 28.</executions>
- 29.</plugin>
- 30.</plugins>
- 31.</build>
- 32.
` - 33.Remove over-aggressive jar filtering:
- 34.```xml
- 35.<!-- WRONG - filters out everything except specific packages -->
- 36.<filters>
- 37.<filter>
- 38.<artifact>*:*</artifact>
- 39.<excludes>
- 40.<exclude>META-INF/*.SF</exclude>
- 41.<exclude>META-INF/*.DSA</exclude>
- 42.<exclude>META-INF/*.RSA</exclude>
- 43.<!-- Too aggressive: excludes actual code -->
- 44.<exclude>**/*.class</exclude>
- 45.</excludes>
- 46.</filter>
- 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.Verify JAR contents:
- 2.```bash
- 3.# Check if class is in the JAR
- 4.jar tf target/myapp-1.0.0.jar | grep ObjectMapper
- 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.Fix dependency scope:
- 2.```xml
- 3.<!-- WRONG - 'provided' means not included in fat JAR -->
- 4.<dependency>
- 5.<groupId>com.fasterxml.jackson.core</groupId>
- 6.<artifactId>jackson-databind</artifactId>
- 7.<version>2.16.0</version>
- 8.<scope>provided</scope>
- 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:treeto check dependency scopes - Add a smoke test that runs
java -jaras part of CI - Consider Spring Boot Maven Plugin for Spring applications (handles shading automatically)
- Use
maven-dependency-plugin:analyzeto detect unused/undeclared dependencies - Never use
minimizeJarwithout thorough integration testing