Cómo resolver java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
Tengo un código que utiliza clases API JAXB que se han proporcionado como parte del JDK en Java 6/7/8. Cuando ejecuto el mismo código con Java 9, en tiempo de ejecución recibo errores que indican que no se pueden encontrar las clases JAXB.
Las clases JAXB se han proporcionado como parte del JDK desde Java 6, entonces, ¿por qué Java 9 ya no puede encontrar estas clases?
Las API JAXB se consideran API de Java EE y, por lo tanto, ya no están contenidas en la ruta de clase predeterminada en Java SE 9. En Java 11, se eliminan por completo del JDK.
Java 9 introduce los conceptos de módulos y, de forma predeterminada, el java.se
módulo agregado está disponible en la ruta de clases (o más bien, en la ruta del módulo). Como su nombre lo indica, el java.se
módulo agregado no incluye las API de Java EE que tradicionalmente se incluyen con Java 6/7/8.
Afortunadamente, estas API de Java EE que se proporcionaron en JDK 6/7/8 todavía están en el JDK, pero simplemente no están en el classpath de forma predeterminada. Las API de Java EE adicionales se proporcionan en los siguientes módulos:
java.activation
java.corba
java.transaction
java.xml.bind << This one contains the JAXB APIs
java.xml.ws
java.xml.ws.annotation
Solución rápida y sucia: (solo JDK 9/10)
Para que las API JAXB estén disponibles en tiempo de ejecución, especifique la siguiente opción de línea de comandos:
--add-modules java.xml.bind
¡¡¡Pero todavía necesito que esto funcione con Java 8!!!
Si intenta especificar --add-modules
con un JDK anterior, explotará porque es una opción no reconocida. Sugiero una de dos opciones:
- Puede configurar cualquier opción exclusiva de Java 9+ utilizando la
JDK_JAVA_OPTIONS
variable de entorno. El iniciador de Java 9+ lee automáticamente esta variable de entornojava
. - Puede agregar
-XX:+IgnoreUnrecognizedVMOptions
para hacer que la JVM ignore silenciosamente las opciones no reconocidas, en lugar de explotar. ¡Pero cuidado! La JVM ya no validará ningún otro argumento de línea de comandos que utilice. Esta opción funciona con Oracle/OpenJDK así como con IBM JDK (a partir de JDK 8sr4).
Solución rápida alternativa: (solo JDK 9/10)
Tenga en cuenta que puede hacer que todos los módulos Java EE anteriores estén disponibles en tiempo de ejecución especificando la --add-modules java.se.ee
opción. El java.se.ee
módulo es un módulo agregado que incluye, java.se.ee
además de los módulos API de Java EE anteriores. Tenga en cuenta que esto no funciona en Java 11 porque java.se.ee
se eliminó en Java 11.
Solución adecuada a largo plazo: (JDK 9 y posteriores)
Todos los módulos de la API de Java EE enumerados anteriormente están marcados @Deprecated(forRemoval=true)
porque su eliminación está programada en Java 11 . Por lo tanto, el --add-module
enfoque ya no funcionará en Java 11 listo para usar.
Lo que deberá hacer en Java 11 y posteriores es incluir su propia copia de las API de Java EE en la ruta de clases o la ruta del módulo. Por ejemplo, puede agregar las API JAX-B como una dependencia de Maven como esta:
<!-- API, java.xml.bind module -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.2</version>
</dependency>
<!-- Runtime, com.sun.xml.bind module -->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.2</version>
</dependency>
Consulte la página de implementación de referencia de JAXB para obtener más detalles sobre JAXB.
Para obtener detalles completos sobre la modularidad de Java, consulte JEP 261: Sistema de módulos
En julio de 2022, la última versión de bind-api y jaxb-runtime es 4.0.0. Entonces también puedes usar
<version>4.0.0</version>
...dentro de esas cláusulas de dependencia. Pero si lo hace, los nombres de los paquetes cambiarán de javax.xml.bind...
a jakarta.xml.bind...
. Deberá modificar su código fuente para utilizar estas versiones posteriores de los JAR.
Para desarrolladores de Gradle o Android Studio: (JDK 9 y posteriores)
Agregue las siguientes dependencias a su build.gradle
archivo:
dependencies {
// JAX-B dependencies for JDK 9+
implementation "jakarta.xml.bind:jakarta.xml.bind-api:2.3.2"
implementation "org.glassfish.jaxb:jaxb-runtime:2.3.2"
}
En mi caso (frasco de grasa de arranque de primavera), simplemente agrego lo siguiente a pom.xml.
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
Solución limpia para todos los JDK >= 9
Necesitas agregar dos dependencias a tu compilación.
- el jaxb-api
- una implementación de jaxb
Como implementación, elegí utilizar la implementación de referencia de glassfish para deshacerme de las antiguas clases/bibliotecas de com.sun. Como resultado, agregué en mi compilación maven
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.1</version>
</dependency>
Tenga en cuenta que a partir de la versión 2.3.1 ya no es necesario agregar javax.activation. (ver https://github.com/eclipse-ee4j/jaxb-ri/issues/1222 )