Статьи

Как решить java.lang.NoClassDefFoundError: Как решить — Часть 2

 



Эта статья является второй частью нашей серии устранения неполадок NoClassDefFoundError. Он сфокусирует и опишет простейший тип проблемы NoClassDefFoundError. Эта статья идеально подходит для начинающих Java, и я настоятельно рекомендую вам скомпилировать и запустить образец Java-программы самостоятельно.

Следующий формат письма будет использоваться идти вперед и предоставит вам:

       
Описание проблемного случая и типа NoClassDefFoundError
       
Пример Java-программы, «имитирующей» проблемный случай
       
Представление цепочки ClassLoader
       
Рекомендации и стратегии решения

Проблема NoClassDefFoundError, вариант 1 — отсутствует файл JAR

Первый проблемный случай, который мы рассмотрим, связан с проблемой упаковки программ Java и / или пути к классам. Типичная Java-программа может включать в себя один или несколько файлов JAR, созданных во время компиляции. NoClassDefFoundError часто можно наблюдать , когда вы забыли добавить JAR файл (ы) , содержащий классы Java , на которые ссылается ваш Java или Java EE приложения .

Этот тип проблемы обычно не трудно решить, если проанализировать исключение Java и пропущенное имя класса Java.

Пример Java-программы

Следующая простая Java-программа разделена, как показано ниже:

       
Основная Java-программа
NoClassDefFoundErrorSimulator
       
вызывающий Java класс
CallerClassA
       
Ссылочный Java-класс
ReferencingClassA
       
класс утилит для ClassLoader и средства ведения журналов
JavaEETrainingUtil

Эта программа просто пытается создать новый экземпляр и выполнить метод класса Java
CallerClassA,
который ссылается на класс
ReferencingClassA.
Он продемонстрирует, как простая проблема с classpath может вызвать NoClassDefFoundError. Программа также отображает сведения о текущей цепочке загрузчиков классов во время загрузки классов, чтобы помочь вам отслеживать этот процесс. Это будет особенно полезно для будущих и более сложных проблемных ситуаций при работе с более крупными цепями загрузчиков классов.
#### NoClassDefFoundErrorSimulator.java
package org.ph.javaee.training1;

import org.ph.javaee.training.util.JavaEETrainingUtil;

/**
 * NoClassDefFoundErrorTraining1
 * @author Pierre-Hugues Charbonneau
 *
 */
public class NoClassDefFoundErrorSimulator {
       
       
        /**
         * @param args
         */
        public static void main(String[] args) {
               System.out.println("java.lang.NoClassDefFoundError Simulator - Training 1");
               System.out.println("Author: Pierre-Hugues Charbonneau");
               System.out.println("http://javaeesupportpatterns.blogspot.com");
              
               // Print current Classloader context
               System.out.println("\nCurrent ClassLoader chain: "+JavaEETrainingUtil.getCurrentClassloaderDetail());
              
               // 1. Create a new instance of CallerClassA
               CallerClassA caller = new CallerClassA();
              
               // 2. Execute method of the caller
               caller.doSomething();
              
               System.out.println("done!");
        }
}
#### CallerClassA.java
package org.ph.javaee.training1;

import org.ph.javaee.training.util.JavaEETrainingUtil;

/**
 * CallerClassA
 * @author Pierre-Hugues Charbonneau
 *
 */
public class CallerClassA {
       
        private final static String CLAZZ = CallerClassA.class.getName();
       
        static {
               System.out.println("Classloading of "+CLAZZ+" in progress..."+JavaEETrainingUtil.getCurrentClassloaderDetail());
        }
       
        public CallerClassA() {
               System.out.println("Creating a new instance of "+CallerClassA.class.getName()+"...");
        }
       
        public void doSomething() {
              
               // Create a new instance of ReferencingClassA
               ReferencingClassA referencingClass = new ReferencingClassA();             
        }
}

#### ReferencingClassA.java
package org.ph.javaee.training1;

import org.ph.javaee.training.util.JavaEETrainingUtil;

/**
 * ReferencingClassA
 * @author Pierre-Hugues Charbonneau
 *
 */
public class ReferencingClassA {

        private final static String CLAZZ = ReferencingClassA.class.getName();
       
        static {
               System.out.println("Classloading of "+CLAZZ+" in progress..."+JavaEETrainingUtil.getCurrentClassloaderDetail());
        }
       
        public ReferencingClassA() {
               System.out.println("Creating a new instance of "+ReferencingClassA.class.getName()+"...");
        }
       
        public void doSomething() {
               //nothing to do...
        }
}


#### JavaEETrainingUtil.java
package org.ph.javaee.training.util;

import java.util.Stack;
import java.lang.ClassLoader;

/**
 * JavaEETrainingUtil
 * @author Pierre-Hugues Charbonneau
 *
 */
public class JavaEETrainingUtil {
       
        /**
         * getCurrentClassloaderDetail
         * @return
         */
        public static String getCurrentClassloaderDetail() {
              
               StringBuffer classLoaderDetail = new StringBuffer();       
               Stack<ClassLoader> classLoaderStack = new Stack<ClassLoader>();
              
               ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
              
               classLoaderDetail.append("\n-----------------------------------------------------------------\n");
              
               // Build a Stack of the current ClassLoader chain
               while (currentClassLoader != null) {
              
                       classLoaderStack.push(currentClassLoader);
                      
                       currentClassLoader = currentClassLoader.getParent();
               }
              
               // Print ClassLoader parent chain
               while(classLoaderStack.size() > 0) {
                      
                       ClassLoader classLoader = classLoaderStack.pop();
                      
                       // Print current                     
                       classLoaderDetail.append(classLoader);
                      
                       if (classLoaderStack.size() > 0) {
                              classLoaderDetail.append("\n--- delegation ---\n");                               
                       } else {
                              classLoaderDetail.append(" **Current ClassLoader**");
                       }
               }
              
               classLoaderDetail.append("\n-----------------------------------------------------------------\n");
              
               return classLoaderDetail.toString();
        }
}
 


Проблема воспроизведения
Чтобы повторить проблему, мы просто «добровольно» опустим один из файлов JAR из пути к классам, который содержит ссылочный класс Java
ReferencingClassA.

Программа на Java упакована согласно ниже:

       
MainProgram.jar (содержит
NoClassDefFoundErrorSimulator.class
и
JavaEETrainingUtil.class
)
       
CallerClassA.jar (содержит
CallerClassA.class
)
       
ReferencingClassA.jar (содержит
ReferencingClassA.class
)

Теперь давайте запустим программу как есть:
## Базовая линия (нормальное исполнение)
.\bin>java -classpath CallerClassA.jar;ReferencingClassA.jar;MainProgram.jar org.ph.javaee.training1.NoClassDefFoundErrorSimulator

java.lang.NoClassDefFoundError Simulator - Training 1
Author: Pierre-Hugues Charbonneau
http://javaeesupportpatterns.blogspot.com

Current ClassLoader chain:
-----------------------------------------------------------------
sun.misc.Launcher$ExtClassLoader@17c1e333
--- delegation ---
sun.misc.Launcher$AppClassLoader@214c4ac9 **Current ClassLoader**
-----------------------------------------------------------------

Classloading of org.ph.javaee.training1.CallerClassA in progress...
-----------------------------------------------------------------
sun.misc.Launcher$ExtClassLoader@17c1e333
--- delegation ---
sun.misc.Launcher$AppClassLoader@214c4ac9 **Current ClassLoader**
-----------------------------------------------------------------

Creating a new instance of org.ph.javaee.training1.CallerClassA...
Classloading of org.ph.javaee.training1.ReferencingClassA in progress...
-----------------------------------------------------------------
sun.misc.Launcher$ExtClassLoader@17c1e333
--- delegation ---
sun.misc.Launcher$AppClassLoader@214c4ac9 **Current ClassLoader**
-----------------------------------------------------------------

Creating a new instance of org.ph.javaee.training1.ReferencingClassA...
done!

Для начального запуска (базовый уровень) основная программа смогла создать новый экземпляр
CallerClassA
и успешно выполнить его метод; включая успешную загрузку класса
ссылочного
класса ReferencingClassA .

## Ошибка запуска воспроизведения (с удалением
ReferencingClassA.jar
)
../bin>java -classpath CallerClassA.jar;MainProgram.jar org.ph.javaee.training1.NoClassDefFoundErrorSimulator

java.lang.NoClassDefFoundError Simulator - Training 1
Author: Pierre-Hugues Charbonneau
http://javaeesupportpatterns.blogspot.com

Current ClassLoader chain:
-----------------------------------------------------------------
sun.misc.Launcher$ExtClassLoader@17c1e333
--- delegation ---
sun.misc.Launcher$AppClassLoader@214c4ac9 **Current ClassLoader**
-----------------------------------------------------------------

Classloading of org.ph.javaee.training1.CallerClassA in progress...
-----------------------------------------------------------------
sun.misc.Launcher$ExtClassLoader@17c1e333
--- delegation ---
sun.misc.Launcher$AppClassLoader@214c4ac9 **Current ClassLoader**
-----------------------------------------------------------------

Creating a new instance of org.ph.javaee.training1.CallerClassA...
Exception in thread "main" java.lang.NoClassDefFoundError: org/ph/javaee/training1/ReferencingClassA
        at org.ph.javaee.training1.CallerClassA.doSomething(CallerClassA.java:25)
        at org.ph.javaee.training1.NoClassDefFoundErrorSimulator.main(NoClassDefFoundErrorSimulator.java:28)
Caused by: java.lang.ClassNotFoundException: org.ph.javaee.training1.ReferencingClassA
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 2 more
 

Что произошло? Удаление ReferencingClassA.jar, содержащего
ReferencingClassA
, не позволило текущему загрузчику классов найти этот ссылочный Java-класс во время выполнения, что привело к ClassNotFoundException и NoClassDefFoundError.

Это типичное исключение, которое вы получите, если пропустите JAR-файл (-ы) из вашего начального пути к классу Java или в EAR / WAR для приложений, связанных с Java EE.

ClassLoader view

Теперь давайте рассмотрим цепочку ClassLoader, чтобы вы могли правильно понять этот проблемный случай. Как вы видели из журнала вывода программ Java, были найдены следующие загрузчики классов Java:
Classloading of org.ph.javaee.training1.CallerClassA in progress...
-----------------------------------------------------------------
sun.misc.Launcher$ExtClassLoader@17c1e333
--- delegation ---
sun.misc.Launcher$AppClassLoader@214c4ac9 **Current ClassLoader**
-----------------------------------------------------------------

** Обратите внимание, что загрузчик классов Java начальной загрузки отвечает за загрузку основных классов JDK и написан на собственном коде **

## sun.misc.Launcher $ AppClassLoader
Это загрузчик системных классов, отвечающий за загрузку кода нашего приложения, найденного по пути к классам Java, указанному при запуске.

## sun.misc.Launcher $ ExtClassLoader
Это загрузчик классов расширений, отвечающий за загрузку кода в каталогах расширений (<JAVA_HOME> / lib / ext или любой другой каталог, указанный в системном свойстве java.ext.dirs).

Как вы можете видеть из вывода журнала Java-программы, загрузчик классов расширений является фактическим супер-родителем загрузчика системных классов. Наш образец Java-программы был загружен на уровне загрузчика системного класса. Обратите внимание, что эта цепочка загрузчиков классов очень проста в этом проблемном случае, так как мы не создавали загрузчики дочерних классов на данном этапе. Это будет рассмотрено в следующих статьях.

Рекомендации и стратегии решения

Теперь найдите ниже мои рекомендации и стратегии решения для проблемы 1 NoClassDefFoundError:

       
Просмотрите ошибку java.lang.NoClassDefFoundError и определите отсутствующий класс Java.
       
Проверьте и найдите отсутствующий класс Java в вашей среде компиляции / сборки.
       
Определите, отсутствует ли класс Java из кода вашего приложения, API третьей части или даже самого контейнера Java EE. Проверьте, где / где должны быть найдены отсутствующие JAR-файлы
       
Найдя, проверьте вашу среду выполнения Java classpath на наличие опечаток или отсутствующих файлов JAR.
       
Если проблема вызвана приложением Java EE, выполните те же действия, что и выше, но проверьте упаковку файла EAR / WAR на предмет отсутствия JAR и других зависимостей файла библиотеки, таких как MANIFEST.

Пожалуйста, не стесняйтесь оставлять любые вопросы или комментарии. Часть 3 будет доступна в ближайшее время.