Статьи

Как решить 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.


#### 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!");
        }
}
 #### 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;
        }
}


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

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

// 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)

## Baseline (normal execution)
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
без проблем.

## Problem reproduction run (static variable initializer failure)

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!

 

## Problem reproduction run (static block initializer failure)

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, связанных с проблемами загрузчика классов.