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