Статьи

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

Эта статья является второй частью нашей серии устранения неполадок NoClassDefFoundError. Посмотрите на часть 1 . Он сфокусирует и опишет простейший тип проблемы 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. Программа также отображает сведения о текущей цепочке загрузчиков классов во время загрузки классов, чтобы помочь вам отслеживать этот процесс. Это будет особенно полезно для будущих и более сложных проблемных ситуаций при работе с более крупными цепями загрузчиков классов.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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!");
        }
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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();            
        }
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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...
        }
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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)

Теперь давайте запустим программу как есть:

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

## Базовая линия (нормальное исполнение)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
..\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!

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

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

ClassLoader view

Теперь давайте рассмотрим цепочку ClassLoader, чтобы вы могли правильно понять этот проблемный случай. Как вы видели из журнала вывода программ Java, были найдены следующие загрузчики классов Java:

1
2
3
4
5
6
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 будет доступна в ближайшее время.

Ссылка: java.lang.NoClassDefFoundError: Как решить — часть 2 от нашего партнера по JCG Пьера-Хьюга Шарбонно из блога по шаблонам поддержки Java EE и учебному пособию по Java .