今后将使用以下书写格式,并为您提供:
–问题案例的描述和NoClassDefFoundError的类型
–示例Java程序“模拟”问题情况 – ClassLoader链视图 –建议和解决策略
NoClassDefFoundError问题案例1 –缺少JAR文件
我们将介绍的第一个问题案例与Java程序包装和/或类路径问题有关。 典型的Java程序可以包含一个或多个在编译时创建的JAR文件。 当您忘记添加包含Java或Java EE应用程序引用的Java类的JAR文件时,通常会观察到NoClassDefFoundError。
一旦您分析了Java异常并缺少Java类名,通常就不难解决这种类型的问题。
示例Java程序
以下简单的Java程序按以下方式拆分:
–主Java程序NoClassDefFoundErrorSimulator
–调用者Java类CallerClassA –引用Java类ReferencingClassA –用于ClassLoader和日志记录相关设施的util类JavaEETrainingUtil
这个程序很简单,它试图创建一个新实例并执行一个Java类CallerClassA的方法,该方法引用了ReferencingClassA类。 它将演示一个简单的类路径问题如何触发NoClassDefFoundError。 该程序还在类加载时显示当前类加载器链的详细信息,以帮助您跟踪此过程。 当处理更大的类加载器链时,这对于将来和更复杂的问题案例特别有用。
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 contextSystem.out.println("\nCurrent ClassLoader chain: "+JavaEETrainingUtil.getCurrentClassloaderDetail());// 1. Create a new instance of CallerClassACallerClassA caller = new CallerClassA();// 2. Execute method of the callercaller.doSomething();System.out.println("done!");}
}
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 ReferencingClassAReferencingClassA referencingClass = new ReferencingClassA(); }
}
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...}
}
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 chainwhile (currentClassLoader != null) {classLoaderStack.push(currentClassLoader);currentClassLoader = currentClassLoader.getParent();}// Print ClassLoader parent chainwhile(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();}
}
问题重现
为了重现该问题,我们将简单地“自愿”从包含引用Java类ReferencingClassA的类路径中省略其中一个JAR文件。
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.NoClassDefFoundErrorSimulatorjava.lang.NoClassDefFoundError Simulator - Training 1
Author: Pierre-Hugues Charbonneau
http://javaeesupportpatterns.blogspot.comCurrent 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的ReferencingClassA.jar确实阻止了当前类加载器在运行时定位此引用Java类,从而导致ClassNotFoundException和NoClassDefFoundError。
如果您从Java启动类路径中或Java EE相关应用程序的EAR / WAR中省略JAR文件,这将是典型的异常。
ClassLoader视图
现在,让我们回顾一下ClassLoader链,以便您可以正确地了解这种问题情况。 从Java程序输出日志记录中可以看到,找到了以下Java ClassLoader:
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程序是在系统类加载器级别加载的。 请注意,对于这种问题情况,该类加载器链非常简单,因为我们此时尚未创建子类加载器。 这将在以后的文章中介绍。
建议和解决策略
现在在下面找到我对NoClassDefFoundError问题案例1的建议和解决策略:
–检查java.lang.NoClassDefFoundError错误并确定缺少的Java类
-在编译/构建环境中验证并找到丢失的Java类
-确定缺少的Java类是来自应用程序代码,第三方API还是Java EE容器本身。 验证预期在哪里找到丢失的JAR文件 –找到后,验证您的运行时环境Java类路径是否存在任何拼写错误或丢失的JAR文件 –如果问题是由Java EE应用程序触发的,请执行与上述相同的步骤,但请验证EAR / WAR文件的包装是否缺少JAR和其他库文件依赖项(如MANIFEST)。
请随时发表任何问题或评论。 第3部分将很快上市。
参考: java.lang.NoClassDefFoundError:如何解决–第2部分,来自我们的JCG合作伙伴 Pierre-Hugues Charbonneau,位于Java EE支持模式和Java教程博客。
翻译自: https://www.javacodegeeks.com/2012/06/this-article-is-part-2-of-our.html