网站免费大全,南昌响应式网站建设,wordpress 新浪图片,商务网站的功能Java开发人员将不得不面对的最困难的问题是类路径错误#xff1a; ClassNotFoundException #xff0c; NoClassDefFoundError #xff0c;Jar Hell#xff0c; Xerces Hell和公司。 在本文中#xff0c;我们将探究这些问题的根本原因#xff0c;并了解最小的工具#… Java开发人员将不得不面对的最困难的问题是类路径错误 ClassNotFoundException NoClassDefFoundError Jar Hell Xerces Hell和公司。 在本文中我们将探究这些问题的根本原因并了解最小的工具 JHades 如何帮助快速解决它们。 我们将看到为什么Maven无法始终防止类路径重复并且 处理地狱的唯一方法 装载机 类加载器链 类加载器的优先级父优先与父末 调试服务器启动问题 用jHades理解Jar Hell 避免类路径问题的简单策略 类路径在Java 9中得到修复吗 处理地狱的唯一方法 类路径问题调试起来很耗时并且往往在最坏的时间和地点发生发布之前通常在开发团队几乎没有访问权限的环境中。 它们也可能发生在IDE级别并成为生产力降低的根源。 我们的开发人员往往会及早发现这些问题这是通常的回答 让我们尝试为我们节省一些时间并深入探讨这一点。 这些类型的问题很难通过反复试验来解决。 解决这些问题的唯一真正方法是真正了解正在发生的事情 但是从哪里开始呢 事实证明Jar Hell问题比其看起来要简单并且仅需几个概念即可解决它们。 最后导致Jar Hell问题的常见根本原因是 一个罐子不见了 一个罐子太多了 一个班级在什么地方不可见 但是如果这么简单那么为什么类路径问题很难调试 Jar Hell堆栈跟踪不完整 原因之一是类路径问题的堆栈跟踪缺少许多信息来解决问题。 以下面的堆栈跟踪为例 java.lang.IncompatibleClassChangeError:
Class org.jhades.SomeServiceImpl does not implement
the requested interface org.jhades.SomeService org.jhades.TestServlet.doGet(TestServlet.java:19) 它说一个类没有实现某个接口。 但是如果我们查看类源代码 public class SomeServiceImpl implements SomeService { Overridepublic void doSomething() {System.out.println( Call successful! );}
} 好了该类显然实现了缺少的接口 那么发生了什么呢 问题是堆栈跟踪缺少很多信息 这些信息对于理解该问题至关重要。 堆栈跟踪可能应该包含这样的错误消息我们将了解这是什么意思 类SomeServiceImpl类加载器/路径/到/ Tomcat的/ lib中不实现接口SomeService从类加载器加载的Tomcat - Web应用程序- /路径/到/ Tomcat的/ web应用/测试 这至少是从哪里开始的指示 刚学习Java的人至少会知道对于了解正在发生的事情必不可少的是类加载器的概念。 很明显涉及的一个类不是从WAR加载的而是以某种方式从服务器上的某个目录 SomeServiceImpl 加载的。 什么是类加载器 首先类加载器只是Java类更确切地说是运行时类的实例。 它不是 JVM不可访问的内部组件例如垃圾收集器。 以Tomcat的WebAppClassLoader为例这里是javadoc 。 如您所见它只是一个普通的Java类如果需要我们甚至可以编写我们自己的类加载器。 ClassLoader都可以用作类加载器。 类加载器的主要职责是知道类文件的位置然后根据JVM的要求加载类。 一切都链接到类加载器 JVM中的每个对象都通过getClass()链接到其类而每个类都通过getClassLoader()链接到类加载器。 这意味着 JVM中的每个对象都链接到一个类加载器 让我们看看如何使用此事实对类路径错误方案进行故障排除。 如何查找类文件的实际位置 让我们来看一个对象看看它的类文件在文件系统中的位置 System.out.println(service.getClass() .getClassLoader().getResource(org/jhades/SomeServiceImpl.class)); 这是类文件的完整路径 jar:file:/Users/user1/.m2/repository/org/jhades/jar-2/1.0-SNAPSHOT/jar-2-1.0-SNAPSHOT.jar!/org/jhades/SomeServiceImpl.class jar:file:/Users/user1/.m2/repository/org/jhades/jar-2/1.0-SNAPSHOT/jar-2-1.0-SNAPSHOT.jar!/org/jhades/SomeServiceImpl.class 如我们所见类加载器只是一个运行时组件它知道文件系统中查找类文件的位置以及如何加载它们。 但是如果类加载器找不到给定的类会发生什么 类加载器链 在JVM中默认情况下如果类加载器未找到类则它将向其父类加载器询问相同的类依此类推。 这一直持续到JVM引导类加载器稍后将对此进行详细介绍。 这个类加载器链是类加载器委托链 。 类加载器的优先级父优先与父末 一些类加载器将请求立即委派给父类加载器而无需先在其自己的已知目录集中搜索类文件。 据说在此模式下运行的类加载器处于“ 父优先”模式。 如果类加载器首先在本地查找类并且仅在查询父类如果找不到该类之后才查找则该类加载器被称为在“上一个父模式”下工作。 所有应用程序都有类加载程序链吗 甚至最简单的Hello World主方法也具有3个类加载器 应用程序类加载器负责加载应用程序类父级优先 Extensions类加载器它从$JAVA_HOME/jre/lib/ext 父先加载jar Bootstrap类加载器用于加载JDK附带的任何类例如java.lang.String 无父类加载器 WAR应用程序的类加载器链是什么样的 对于Tomcat或Websphere之类的应用程序服务器类加载器链的配置与简单的Hello World主方法程序不同。 以Tomcat类加载器链为例 在这里我们希望每个WAR都在WebAppClassLoader运行该WebAppClassLoader在父级末尾模式下工作也可以将其设置为父级末尾。 通用类加载器加载在服务器级别安装的库。 Servlet规范对类加载有何看法 Servlet容器规范仅定义了类加载器链行为的一小部分 WAR应用程序在其自己的应用程序类加载器上运行可以与其他应用程序共享或不与其他应用程序共享 WEB-INF/classes的文件优先于其他所有文件 在那之后任何人都可以猜测 其余的完全开放给容器提供商解释。 为什么在供应商之间没有通用的类加载方法 通常默认情况下通常将诸如Tomcat或Jetty之类的开源容器配置为在WAR中首先查找类然后才在服务器类加载器中搜索。 这允许应用程序使用其自己的库版本来覆盖服务器上可用的库。 大型铁服务器呢 诸如Websphere之类的商业产品将尝试向您“出售”自己的服务器提供的库这些库默认情况下优先于WAR上安装的库。 假定您购买了该服务器并且还希望使用它提供的JEE库和版本那么通常不会发生这种情况。 这给部署到某些商业产品带来了极大的麻烦因为它们的行为方式不同于开发人员用来在其工作站中运行应用程序的Tomcat或Jetty。 我们将在此方面找到进一步的解决方案。 常见问题重复的类版本 目前您可能有一个很大的问题 如果WAR中有两个罐子包含完全相同的类怎么办 答案是行为是不确定的 只有在运行时才会选择两个类之一 。 选择哪一个取决于类加载器的内部实现无法预先知道。 但是幸运的是如今大多数项目都使用MavenMaven通过确保仅将给定jar的一个版本添加到WAR中来解决此问题。 因此Maven项目可以不受这种特定类型的Jar Hell的影响对吗 为什么Maven不能防止类路径重复 不幸的是Maven无法在所有Jar Hell情况下提供帮助。 实际上许多不使用某些质量控制插件的Maven项目在类路径上都可以有数百个重复的类文件我看到中继有500多个重复项。 这有几个原因 图书馆出版商有时会更改罐子的工件名称发生这种情况是由于品牌重塑或其他原因。 以JAXB jar为例。 Maven无法将这些工件识别为同一罐子 某些jar带有或不带有依赖项发布一些库提供程序提供jar的“带有依赖项”版本其中包括其他jar。 如果两个版本都具有传递依赖则最终将导致重复。 有些类在jar之间复制有些库创建者在遇到某个类的需要时只会从另一个项目中获取它然后将其复制到新的jar中而不更改包名。 所有的班级文件重复都有危险吗 如果重复的类文件存在于同一个类加载器中并且两个重复的类文件完全相同那么首先选择哪个是无关紧要的–这种情况并不危险。 如果两个类文件都在同一个类加载器中并且它们不相同则无法在运行时选择一个这是有问题的并且在部署到不同环境时会表现出来。 如果类文件位于两个不同的类加载器中则永远不会将它们视为相同请参见后面的类标识危机部分。 如何避免WAR类路径重复 例如可以使用Maven Enforcer插件来避免此问题并启用“ 禁止重复类”的额外规则。 您也可以使用JHades WAR重复类报告快速检查您的WAR是否干净。 该工具可以过滤“无害”重复项相同的类文件大小。 但是即使是干净的WAR也会存在部署问题类丢失从服务器而不是WAR那里获取的类以及版本错误类强制转换异常等。 使用JHades调试类路径 类路径问题通常在应用程序服务器启动时出现这是一个特别糟糕的时刻尤其是在部署到访问受限的环境中时。 JHades是帮助处理Jar Hell的工具免责声明我写的。 它是单个Jar除了JDK7本身之外没有任何依赖关系。 这是一个如何使用它的示例 new JHades().printClassLoaders().printClasspath().overlappingJarsReport().multipleClassVersionsReport().findClassByName(org.jhades.SomeServiceImpl) 这会将类加载器链罐子重复类等打印到屏幕上。 调试服务器启动问题 在服务器无法正常启动的情况下JHades可以很好地工作。 提供了一个Servlet侦听器即使在应用程序的任何其他组件开始运行之前该侦听器也可以打印类路径调试信息。 ClassCastException和类身份危机 对Jar Hell进行故障排除时请注意ClassCastExceptions 。 在JVM中不仅通过完全限定的类名来标识类而且还通过其类加载器来标识该类。 这是违反直觉的但事后看来是有道理的我们可以使用相同的包和名称创建两个不同的类将它们放在两个jar中放入两个不同的类加载器中。 可以说一个扩展了ArrayList 另一个是Map 。 因此这些类完全不同尽管名称相同并且不能相互转换 运行时将抛出CCE以防止发生这种潜在的错误情况因为无法保证这些类是可强制转换的。 将类加载器添加到类标识符是Java早期发生的类身份危机的结果。 避免类路径问题的策略 说起来容易做起来难但是避免与类路径相关的部署问题的最佳方法是在“ 上一个下一个”模式下运行生产服务器。 这样WAR的类版本优先于服务器上的类版本并且在生产环境和开发人员工作站中使用了相同的类这很可能在使用TomcatJetty或其他开放源代码的Parent Last服务器。 在某些服务器如Websphere中这还不够并且您还必须在清单文件中提供特殊属性以显式关闭某些库例如JAX-WS。 修复Java 9中的类路径 在Java 9中类路径已完全通过新的Jigsaw模块化系统进行了改进。 在Java 9中可以将jar声明为模块它将在其自己的隔离类加载器中运行该类加载器以OSGI方式从其他类似的模块类加载器读取类文件。 如果需要这将允许同一版本的Jar的多个版本共存。 结论 最后Jar Hell问题并不是像最初看起来那样低级或难以解决。 都是关于zip文件jar在某些目录中存在/不存在如何查找那些目录以及如何在访问受限的环境中调试类路径。 通过了解有限的概念集例如类加载器类加载器链和“父优先/父末”模式可以有效解决这些问题。 外部链接 这份演讲“您真的从ZeroTurnaround的Jevgeni Kabanov JRebel公司 获得类加载器”是有关Jar Hell以及与不同类型的类路径相关的异常的重要资源。 翻译自: https://www.javacodegeeks.com/2014/10/jar-hell-made-easy-demystifying-the-classpath-with-jhades.html