لماذا تصرخ Java "NoClassDefFoundError: javax/xml/bind/JAXBException" وماذا تفعل حيال ذلك في 2025؟
\n\nهل سبق لك أن واجهت موقفًا حيث كان تطبيق Java الخاص بك، الذي كان يعمل كالساعة بالأمس، يتعطل اليوم مع خطأ مخيف java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException؟ إذا كان الأمر كذلك - فأنت لست وحدك. هذا السؤال جمع آلاف المشاهدات على Stack Overflow، والطلب على الحل لا ينخفض حتى في عام 2025. المشكلة مرتبطة بتغييرات جوهرية في نظام Java البيئي حدثت قبل عدة سنوات، ولكنها لا تزال تؤثر على المطورين حتى الآن.
في هذه المقالة، لن نقدم لك "السمكة" فقط - بل سنشرح لماذا يحدث هذا الخطأ، ونعرض ثلاث طرق موثوقة لإصلاحه. ستتعلم كيفية تكوين Maven أو Gradle أو وسائط JVM بشكل صحيح لجعل تطبيقك يعمل مرة أخرى. هيا بنا!
\n\nجذر المشكلة: Java 9 ونظام الوحدات
\n\nبدأ كل شيء مع Java 9 (التي صدرت في عام 2017). في هذا الإصدار، قدمت Oracle نظام الوحدات (Project Jigsaw). تم نقل العديد من الحزم التي كانت جزءًا من المكتبة القياسية (JDK) إلى وحدات منفصلة أو تمت إزالتها بالكامل. كانت إحدى هذه الحزم "المنبوذة" هي حزمة javax.xml.bind (JAXB - Java Architecture for XML Binding).
في Java 8 والإصدارات الأقدم، كان JAXB متاحًا "مباشرة من الصندوق". بدءًا من Java 9، أصبح اختياريًا. وفي Java 11، تمت إزالته تمامًا من JDK. إذا كنت تكتب كودًا يستخدم javax.xml.bind.JAXBContext أو JAXBException أو Marshaller، وتقوم بتشغيله على Java 11+، فستحصل بالتأكيد على NoClassDefFoundError.
هام: يختلف NoClassDefFoundError عن ClassNotFoundException. الأول يحدث عندما يكون الفصل متاحًا في مرحلة التجميع، ولكنه يختفي أثناء وقت التشغيل. الثاني يحدث عندما لا يتم العثور على الفصل على الإطلاق.
سيناريو: كيف تبدو المشكلة عمليًا
\n\nتخيل أن لديك فئة بسيطة للعمل مع XML:
\n\nimport javax.xml.bind.JAXBContext;\nimport javax.xml.bind.JAXBException;\nimport javax.xml.bind.Marshaller;\n\npublic class XmlProcessor {\n\n public void convertToXml(Object obj) throws JAXBException {\n JAXBContext context = JAXBContext.newInstance(obj.getClass());\n Marshaller marshaller = context.createMarshaller();\n marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);\n // ... منطق الإخراج\n }\n}\n\nتقوم بتجميع هذا الكود على Java 8 - كل شيء على ما يرام. ثم تقوم بتحديث JDK إلى الإصدار 11 أو 17، وتشغيل التطبيق وترى:
\n\nException in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException\n at XmlProcessor.convertToXml(XmlProcessor.java:6)\n ...\n\nمألوف؟ الآن دعنا نجد الحل.
\n\nالطريقة 1: إضافة JAXB API كتبعية خارجية (موصى بها)
\n\nالنهج الأكثر صحة وحداثة هو تحديد أن تطبيقك يستخدم JAXB بشكل صريح. للقيام بذلك، تحتاج إلى إضافة المكتبة من خلال نظام البناء (Maven أو Gradle).
\n\nلـ Maven (pom.xml)
\n\nأضف إلى <dependencies>:
<dependency>\n <groupId>jakarta.xml.bind</groupId>\n <artifactId>jakarta.xml.bind-api</artifactId>\n <version>4.0.2</version>\n</dependency>\n<dependency>\n <groupId>org.glassfish.jaxb</groupId>\n <artifactId>jaxb-runtime</artifactId>\n <version>4.0.5</version>\n <scope>runtime</scope>\n</dependency>\n\nلاحظ: مع Jakarta EE 9 (يونيو 2021) تمت إعادة تسمية الحزمة javax.xml.bind إلى jakarta.xml.bind. إذا كان كودك قديمًا ويستخدم javax.*، فلا يزال بإمكانك استخدام