Comment résoudre java.lang.NoClassDefFoundError
L'erreur java.lang.NoClassDefFoundError fait partie des exceptions Java les plus frustrantes : le code compile sans problème, mais plante brutalement à l'exécution. Ce guide explique ses causes et propose une méthode systématique pour la résoudre.
Qu'est-ce que NoClassDefFoundError ?
C'est une erreur (et non une exception cochée) lancée par la JVM quand elle cherche à charger une classe qui existait à la compilation mais qu'elle ne trouve plus à l'exécution, ou dont l'initialisation a échoué.
Exception in thread "main" java.lang.NoClassDefFoundError: com/example/MaClasse
at com.example.Application.main(Application.java:12)
Différence avec ClassNotFoundException
| Erreur | Quand ? | Cause typique |
|---|---|---|
ClassNotFoundException | Chargement dynamique (Class.forName) | Classe absente du classpath |
NoClassDefFoundError | Référence statique dans le bytecode | Classe présente à la compilation mais absente au runtime, OU static initialisation échouée |
Causes les plus fréquentes
1. Un JAR manquant dans le classpath
La cause n° 1. Le code compile parce que l'IDE voit le JAR, mais au lancement en ligne de commande la dépendance n'y est pas.
# Vérifier ce qui est réellement sur le classpath
java -cp target/app.jar:lib/* -verbose:class com.example.Application 2>&1 | head -30
2. Une initialisation statique qui échoue
Si un bloc static ou un champ static lève une exception au premier chargement de la classe, la JVM marque la classe comme erroneous. Toutes les références ultérieures déclenchent NoClassDefFoundError — mais la vraie cause est dans la stack trace précédente, souvent un ExceptionInInitializerError.
public class Config {
// Si le fichier n'existe pas, cette ligne lève NullPointerException
// La classe Config ne sera jamais chargeable
static final Properties PROPS = loadProps("config.properties");
}
3. Un conflit de versions (JAR hell)
Deux versions d'une même bibliothèque dans le classpath. La première chargée gagne, et ses classes peuvent manquer des méthodes attendues par le reste du code.
mvn dependency:tree -Dverbose
./gradlew dependencyInsight --dependency slf4j-api
4. Une dépendance provided non fournie
En Maven, les dépendances marquées <scope>provided</scope> ne sont pas incluses dans le JAR exécutable. Normal pour les API servlet ou JDBC sur un serveur d'applications, problématique ailleurs.
5. Un chargeur de classes différent
Dans les serveurs d'applications (Tomcat, WildFly) ou OSGi, chaque déploiement a son propre ClassLoader. Une classe visible d'un module ne l'est pas forcément d'un autre.
Méthode de diagnostic en 4 étapes
- Lire toute la stack trace, y compris les
Caused by:et les exceptions précédentes dans les logs.ExceptionInInitializerErrorjuste avant indique une initialisation statique fautive. - Identifier le JAR qui devrait contenir la classe. Utilisez
jar tf mon.jar | grep MaClasseou le moteur de recherche mvnrepository.com. - Lister ce qui est sur le classpath :
java -cp ... -XshowSettings:properties 2>&1 | grep java.class.path - Vérifier les conflits avec
mvn dependency:treeougradle dependencies.
Solutions concrètes
Avec Maven
<!-- Vérifiez que le scope n'est pas 'provided' pour un JAR exécutable -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.18.2</version>
</dependency>
Pour créer un fat JAR contenant toutes les dépendances :
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version>
</plugin>
Avec Gradle
dependencies {
implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.2'
}
// Pour un fat jar :
tasks.jar { from(configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }) }
En ligne de commande
# Inclure explicitement tous les JAR du dossier lib/
java -cp "app.jar:lib/*" com.example.Application
Points de vigilance
- Sur Windows, le séparateur de classpath est
;et non:. - Ne mélangez jamais des JAR du JDK et des JAR Java EE (problèmes classiques avec
javax.servlet). - Pour une application Spring Boot, utilisez
mvn spring-boot:runoujava -jar app.jar— jamais le JAR directement dans un classpath manuel.
Dans 90 % des cas, un simple mvn clean package suivi d'une vérification du target/app.jar suffit à résoudre le problème.