邓州做网站,阿里云淘宝客网站建设教程,梅州企业网站建设公司,nas网站怎么做网站在许多开发人员中#xff0c;类加载器是Java语言的底层#xff0c;并且经常被忽略。 在ZeroTurnaround上 #xff0c;我们的开发人员必须生活#xff0c;呼吸#xff0c;饮食#xff0c;喝酒#xff0c;并且几乎与类加载器保持亲密关系#xff0c;才能生产JRebel技术类加载器是Java语言的底层并且经常被忽略。 在ZeroTurnaround上 我们的开发人员必须生活呼吸饮食喝酒并且几乎与类加载器保持亲密关系才能生产JRebel技术该技术在类加载器级别进行交互以提供实时运行时类重装从而避免了冗长的重建/重新包装/重新部署周期。 以下是我们从类加载器中学到的一些知识其中包括一些调试技巧这些技巧将有望为您节省时间和将来的总服务台。 一个类加载器只是一个普通的java对象 是的这并不聪明除了JVM中的系统类加载器之外类加载器只是一个Java对象 这是一个抽象类ClassLoader可以由您创建的类实现。 这是API public abstract class ClassLoader {public Class loadClass(String name);protected Class defineClass(byte[] b);public URL getResource(String name);public Enumeration getResources(String name);public ClassLoader getParent()} 看起来很简单对吧 让我们逐个方法看一下。 中心方法是loadClass它仅使用String类名然后返回实际的Class对象。 如果您以前使用过类加载器则此方法可能是最熟悉的方法因为它是日常编码中使用最多的方法。 defineClass是JVM中的最终方法该方法从网络上的文件或位置获取字节数组并产生相同的结果即Class对象。 类加载器还可以从类路径中找到资源。 它的工作方式与loadClass方法类似。 有两种方法getResource和getResources它们返回一个URL或URL的枚举这些URL或URL的枚举指向资源该资源表示传递给方法的名称。 每个类加载器都有一个父级。 getParent返回与Java继承无关的classloader父类而是一个链表样式的连接。 稍后我们将对此进行更深入的研究。 类加载器是惰性的因此仅在运行时请求类时才加载类。 类是由调用该类的资源加载的因此在运行时一个类可以由多个类加载器加载具体取决于从何处引用它们以及哪个类加载器加载了引用了这些类的类…哎呀我cross地了 让我们看一些代码。 public class A {public void doSmth() {B b new B();b.doSmthElse();}} 在这里我们有一个类A在其方法范围内调用类B的构造函数。 在幕后这是正在发生的事情 A.class.getClassLoader().loadClass(“B”); 最初加载类A的类加载器被调用以加载类B。 类加载器是分层的但是像孩子一样他们并不总是问父母 每个类加载器都有一个父类加载器。 当一个类加载器被要求提供一个类时它通常会直接转到父类加载器首先调用loadClass而后者又会询问它的父类依此类推。 如果要求具有相同父级的两个类加载器加载同一类则父级将只执行一次。 当两个类加载器分别加载同一个类时这将非常麻烦因为这可能会导致问题我们将在后面讨论。 当设计JEE规范时Web类加载器被设计为以相反的方式工作-很棒。 让我们看一下下图作为示例。 模块WAR1有自己的类加载器并且更喜欢自行加载类而不是委托给其父级由App1.ear定义的类加载器。 这意味着不同的WAR模块例如WAR1和WAR2无法看到彼此的类。 App1.ear模块具有自己的类加载器并且是WAR1和WAR2类加载器的父级。 当WAR1和WAR2类加载器需要在层次结构中委派请求时即WAR类加载器范围之外需要一个类时它们将使用App1.ear类加载器。 实际上WAR类会覆盖同时存在的EAR类。 最后EAR类加载器的父级是容器类加载器。 EAR类加载器会将请求委派给容器类加载器但它的执行方式与WAR类加载器不同因为EAR类加载器实际上更喜欢委托而不是本地类。 如您所见这变得非常繁琐并且与普通的JSE类加载行为不同。 平面类路径 我们讨论了系统类加载器如何通过类路径查找已请求的类。 该类路径可能包含目录或JAR文件查找它们的顺序实际上取决于您使用的JVM。 您在类路径上可能需要该类的多个副本或版本但是您将始终在类路径上找到该类的第一个实例。 本质上这只是资源列表这就是为什么将其称为扁平资源。 结果在查找资源时遍历类路径列表通常会比较慢。 当使用相同类路径的应用程序想要使用类的不同版本时可能会发生问题让我们以Hibernate为例。 当类路径上存在两个版本的Hibernate JAR时一个版本不能比一个应用程序的版本路径在另一个应用程序的类路径上更高这意味着两个版本都必须使用相同的版本。 解决此问题的一种方法是使用所有必需的库使应用程序WAR膨胀以便它们使用其本地资源但这会导致难以维护的大型应用程序。 欢迎来到JAR地狱 OSGi在此提供了一种解决方案因为它允许对JAR文件或捆绑软件进行版本控制从而形成一种机制允许连接到特定版本的JAR文件从而避免了平坦的类路径问题。 如何调试类加载错误 NoClassDefFoundError / ClassNotFoundException / ClassNoDefFoundException 因此您遇到了上述错误/异常。 好吧这个班级真的存在吗 不要在IDE中寻找麻烦因为在那儿编译类是必须的因为它必须在那里否则您将获得编译时异常。 这是一个运行时异常因此在运行时我们要查找它说我们缺少的类……但是您从哪里开始呢 考虑下面的代码… Arrays.toString((((URLClassLoader) Test.class.getClassLoader()).getURLs())); 此代码返回Test正在使用的类加载器的类路径上所有jar和目录的数组列表。 现在我们可以看到神秘类应该存在的JAR或位置实际上在类路径上。 如果不存在请添加 如果确实存在请检查JAR /目录以确保您的类确实存在于该位置并在缺少该类时添加它。 这是导致此错误情况的两个典型问题。 NoSuchMethodError / NoSuchFieldError / AbstractMethodError / IllegalAccessError吗 现在变得越来越有趣了 这些都是IncompatibleClassChangeError的所有子类。 我们知道类加载器已经找到了想要的类按名称但是显然它没有找到正确的版本。 在这里我们有一个称为Test的类它正在调用另一个类Util但是BANG –我们遇到了异常 让我们看一下要调试的下一个代码片段 Test.class.getClassLoader().getResource(Util.class.getName().replace(., /) .class); 我们在类Test的类加载器上调用getResource。 这将向我们返回Util资源的URL。 请注意我们已替换了“。” 带有“ /”并在字符串末尾添加“ .class”。 这会将我们正在寻找的类的包和类名从类加载器的角度来看更改为文件系统上的目录结构和文件名-简洁。 这将向我们显示我们已加载的确切类并且可以确保它是正确的版本。 我们可以在命令提示符下在类上使用javap -private来查看字节码并检查实际存在的方法和字段。 您可以轻松地查看该类的结构并验证是您还是疯了的Java运行时 相信我在一个或另一个阶段您都会同时问这两个问题几乎每次都是您 LinkageError / ClassCastException / IllegalAccessError 如果两个不同的类加载器加载同一个类并且它们尝试进行交互则可能会发生这种情况。 是的现在有点毛了。 这可能会导致问题因为我们不知道它们是否将从同一位置加载类。 怎么会这样 让我们看下面的代码它们仍然在Test类中 Factory.instance().sayHello(); 该代码看起来非常干净和安全尚不清楚如何从此行出现错误。 我们正在调用静态工厂方法来获取Test类的实例并在其上调用方法。 让我们看一下该支持图像以显示引发异常的原因。 在这里我们可以看到一个Web类加载器加载了Test类将优先使用本地类因此当它引用一个类时将尽可能由Web类加载器加载。 到目前为止还算简单。 Test类使用Factory类来获取Util类的实例这在Java中是很典型的做法但是Factory类在WAR中并不存在因为它是一个外部库。 这是没有问题的因为Web类加载器可以委托给共享类加载器后者可以看到Factory类。 请注意共享类加载器现在正在加载它自己的Util类版本因为当Factory实例化该类时它使用了共享类加载器如前面的第一个示例所示。 Factory类将Util对象由共享类加载器创建返回给WARWAR然后尝试使用该类并将该类有效地强制转换为同一类的潜在不同版本Web类加载器可见的Util类 。 繁荣 我们可以在两个地方Factory.instance方法和Test类中运行与以前相同的代码以查看每个Util类从何处加载。 Test.class.getClassLoader().getResource(Util.class.getName().replace(., /) .class)); 希望这可以使您对类加载的世界有所了解而不是不了解类加载器现在可以带着恐惧和不确定性来欣赏它 感谢您的阅读并将其制作到最后。 我们都希望您从ZeroTurnaround祝您圣诞快乐新年快乐 编码愉快 参考 在JVM的底层– Java出现日历博客中来自JCG合作伙伴 Simon Maple的类加载器 。 翻译自: https://www.javacodegeeks.com/2012/12/under-the-jvm-hood-classloaders.html