Статьи

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

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

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

Пересмотрен статический инициализатор Java

Язык программирования Java предоставляет вам возможность «статически» инициализировать переменные или блок кода. Это достигается с помощью «статического» идентификатора переменной или использования статического блока {} в заголовке класса Java. Статические инициализаторы гарантированно выполняются только один раз в жизненном цикле JVM и являются поточно-ориентированными по своей конструкции, что делает их использование весьма привлекательным для инициализации статических данных, таких как внутренние кэши объектов, средства ведения журнала и т. Д.

В чем проблема? Я повторю еще раз, статические инициализаторы гарантированно будут выполняться только один раз в жизненном цикле JVM… Это означает, что такой код выполняется во время загрузки класса и никогда не будет выполняться снова, пока вы не перезапустите JVM. Что произойдет, если код, выполненный в это время (время загрузки @Class), завершится необработанным исключением?

Добро пожаловать в проблемный случай java.lang.NoClassDefFoundError # 2!

Проблема NoClassDefFoundError, вариант 2 — статическая ошибка инициализатора

Проблема такого типа возникает после сбоя статического кода инициализатора в сочетании с последовательными попытками создания нового экземпляра затронутого (незагруженного) класса.

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

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

— Основная Java-программа NoClassDefFoundErrorSimulator
— Уязвимый класс Java ClassA
— ClassA предоставляет вам переключатель ON / OFF, позволяющий вам воспроизвести тип проблемы, которую вы хотите изучить

Эта программа просто пытается создать новый экземпляр ClassA 3 раза (один за другим). Он продемонстрирует, что первоначальный сбой статической переменной или инициализатора статического блока в сочетании с последовательной попыткой создания нового экземпляра затронутого класса вызывает триггеры java.lang.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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#### NoClassDefFoundErrorSimulator.java
package org.ph.javaee.tools.jdk7.training2;
 
/**
 * NoClassDefFoundErrorSimulator
 * @author Pierre-Hugues Charbonneau
 *
 */
public class NoClassDefFoundErrorSimulator {
        
        /**
         * @param args
         */
        public static void main(String[] args) {
               System.out.println("java.lang.NoClassDefFoundError Simulator - Training 2");
               System.out.println("Author: Pierre-Hugues Charbonneau");
               System.out.println("http://javaeesupportpatterns.blogspot.com\n\n");             
                              
               try {                
                       // Create a new instance of ClassA (attempt #1)
                       System.out.println("FIRST attempt to create a new instance of ClassA...\n");       
                       ClassA classA = new ClassA();
                       
               } catch (Throwable any) {
                       any.printStackTrace();
               }            
               
               try {                
                       // Create a new instance of ClassA (attempt #2)
                       System.out.println("\nSECOND attempt to create a new instance of ClassA...\n");       
                       ClassA classA = new ClassA();
                       
               } catch (Throwable any) {                  
                       any.printStackTrace();
               }     
               
               try {                
                       // Create a new instance of ClassA (attempt #3)
                       System.out.println("\nTHIRD attempt to create a new instance of ClassA...\n");       
                       ClassA classA = new ClassA();
                       
               } catch (Throwable any) {                  
                       any.printStackTrace();
               }            
               
               System.out.println("\n\ndone!");
        }
}
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
#### ClassA.java
package org.ph.javaee.tools.jdk7.training2;
 
/**
 * ClassA
 * @author Pierre-Hugues Charbonneau
 *
 */
public class ClassA {
        
        private final static String CLAZZ = ClassA.class.getName();
        // Problem replication switch ON/OFF
        private final static boolean REPLICATE_PROBLEM1 = true; // static variable initializer
        private final static boolean REPLICATE_PROBLEM2 = false; // static block{} initializer
        
        // Static variable executed at Class loading time
        private static String staticVariable = initStaticVariable();
        
        // Static initializer block executed at Class loading time
        static {
               
               // Static block code execution...
               if (REPLICATE_PROBLEM2) throw new IllegalStateException("ClassA.static{}: Internal Error!");
        }
        
        public ClassA() {
               System.out.println("Creating a new instance of "+ClassA.class.getName()+"...");
        }
        
        /**
         *
         * @return
         */
        private static String initStaticVariable() {
               
               String stringData = "";
               
               if (REPLICATE_PROBLEM1) throw new IllegalStateException("ClassA.initStaticVariable(): Internal Error!");
               
               return stringData;
        }
}

Проблема воспроизведения

Чтобы повторить проблему, мы просто «добровольно» вызовем сбой статического кода инициализатора. Пожалуйста, просто включите тип проблемы, которую вы хотите изучить, например, ошибка статической переменной или статического блока:

1
2
3
// Problem replication switch ON (true) / OFF (false)
      private final static boolean REPLICATE_PROBLEM1 = true; // static variable initializer
      private final static boolean REPLICATE_PROBLEM2 = false; // static block{} initializer

Теперь давайте запустим программу с обоими переключателями в положении OFF (оба логических значения в false)
## Базовая линия (нормальное исполнение)

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
             java.lang.NoClassDefFoundError Simulator - Training 2
              
             
             
             Author: Pierre-Hugues Charbonneau
              
             
             
             http://javaeesupportpatterns.blogspot.com
              
             
             
           
FIRST attempt to create a new instance of ClassA...
              
             
             
            
Creating a new instance of org.ph.javaee.tools.jdk7.training2.ClassA...
              
             
             
SECOND attempt to create a new instance of ClassA...
              
             
             
Creating a new instance of org.ph.javaee.tools.jdk7.training2.ClassA...
              
             
             
THIRD attempt to create a new instance of ClassA...
              
             
             
Creating a new instance of org.ph.javaee.tools.jdk7.training2.ClassA...
              
             
             
done!

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

## Запуск воспроизведения проблемы (ошибка инициализатора статической переменной)

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
java.lang.NoClassDefFoundError Simulator - Training 2
              
             
             
             Author: Pierre-Hugues Charbonneau
              
             
             
             http://javaeesupportpatterns.blogspot.com
              
             
             
FIRST attempt to create a new instance of ClassA...
              
             
             
java.lang.ExceptionInInitializerError
              
             
             
                     at org.ph.javaee.tools.jdk7.training2.NoClassDefFoundErrorSimulator.main(
             NoClassDefFoundErrorSimulator.java:21
             )
              
             
             
             Caused by:
             java.lang.IllegalStateException
             : ClassA.initStaticVariable(): Internal Error!
              
             
             
                     at org.ph.javaee.tools.jdk7.training2.ClassA.initStaticVariable(
             ClassA.java:37
             )
              
             
             
                     at org.ph.javaee.tools.jdk7.training2.ClassA.<clinit>(
            ClassA.java:16
             )
              
             
             
                     ... 1 more
              
             
             
SECOND attempt to create a new instance of ClassA...
              
             
             
java.lang.NoClassDefFoundError: Could not initialize class org.ph.javaee.tools.jdk7.training2.ClassA
              
             
             
                     at org.ph.javaee.tools.jdk7.training2.NoClassDefFoundErrorSimulator.main(
           NoClassDefFoundErrorSimulator.java:30
             )
              
             
             
THIRD attempt to create a new instance of ClassA...
              
             
             
java.lang.NoClassDefFoundError: Could not initialize class org.ph.javaee.tools.jdk7.training2.ClassA
              
             
             
                     at org.ph.javaee.tools.jdk7.training2.NoClassDefFoundErrorSimulator.main(
           NoClassDefFoundErrorSimulator.java:39>
             )
              
             
             
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
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
java.lang.NoClassDefFoundError Simulator - Training 2
              
             
             
             Author: Pierre-Hugues Charbonneau
              
             
             
             http://javaeesupportpatterns.blogspot.com
              
             
             
              
FIRST attempt to create a new instance of ClassA...
              
             
             
java.lang.ExceptionInInitializerError
              
             
             
                    at org.ph.javaee.tools.jdk7.training2.NoClassDefFoundErrorSimulator.main(
             NoClassDefFoundErrorSimulator.java:21
             )
              
             
             
             Caused by:
             java.lang.IllegalStateException>
             : ClassA.static{}: Internal Error!
              
             
             
                    at org.ph.javaee.tools.jdk7.training2.ClassA.<clinit>(
             ClassA.java:22
             )
              
             
             
                    ... 1 more
              
             
             
SECOND attempt to create a new instance of ClassA...
              
             
             
java.lang.NoClassDefFoundError: Could not initialize class org.ph.javaee.tools.jdk7.training2.ClassA
              
             
             
                    at org.ph.javaee.tools.jdk7.training2.NoClassDefFoundErrorSimulator.main(
             NoClassDefFoundErrorSimulator.java:30
             )
              
             
             
THIRD attempt to create a new instance of ClassA...
              
             
             
java.lang.NoClassDefFoundError: Could not initialize class org.ph.javaee.tools.jdk7.training2.ClassA
              
             
             
                    at org.ph.javaee.tools.jdk7.training2.NoClassDefFoundErrorSimulator.main(
             NoClassDefFoundErrorSimulator.java:39
             )
              
             
             
done!

Что произошло? Как вы можете видеть, первая попытка создать новый экземпляр ClassA инициировала java.lang.ExceptionInInitializerError. Это исключение указывает на сбой нашего статического инициализатора для нашей статической переменной & блока, что именно то, что мы хотели достичь.

Ключевой момент, который необходимо понять, заключается в том, что этот сбой предотвратил загрузку всего класса ClassA. Как вы можете видеть, попытка № 2 и попытка № 3 сгенерировали java.lang.NoClassDefFoundError, почему? Ну, так как первая попытка не удалась, загрузка класса ClassA была предотвращена. Последовательные попытки создать новый экземпляр ClassA в текущем ClassLoader действительно генерировали java.lang.NoClassDefFoundError снова и снова, поскольку ClassA не был найден в текущем ClassLoader.

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

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

Теперь найдите ниже мои рекомендации и стратегии решения для проблемы 2 проблемы NoClassDefFoundError:
— Просмотрите ошибку java.lang.NoClassDefFoundError и определите отсутствующий класс Java.
— Выполните пошаговое выполнение кода для затронутого класса и определите, содержит ли он статический код инициализатора (переменные и статический блок)
— Просмотрите журналы сервера и приложения и определите, возникла ли какая-либо ошибка (например, ExceptionInInitializerError) из кода статического инициализатора. — После подтверждения проанализируйте код еще раз и определите основную причину сбоя кода инициализатора. Возможно, вам придется добавить некоторые дополнительные журналы вместе с надлежащей обработкой ошибок, чтобы предотвратить и лучше обрабатывать будущие сбои вашего кода статического инициализатора в будущем.

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

Часть 4 начнет рассмотрение проблем NoClassDefFoundError, связанных с проблемами загрузчика классов.

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