翻译原文链接

背景

Java平台旨在提供健壮,安全和可扩展的功能,以支持代码和数据的移动性。Java虚拟机(JVM)中的Java ClassLoader是实现这些目标的关键组件。

JVM负责在Java平台上加载和执行代码。它使用ClassLoader将Java类加载到Java运行时环境中。ClassLoader的架构使得在启动时JVM不需要知道将在运行时加载的类的任何信息。几乎所有基于Java的容器(如EJB或servlet容器)都实现了自定义ClassLoader,以支持热部署和运行时平台可扩展性等功能。 在实现此类基于Java的容器时,深入了解ClassLoaders对于开发人员非常重要。

对于开发部署在这些容器上的组件的企业开发人员,这些知识将帮助您了解容器的工作方式以及调试问题。 本文介绍了Java ClassLoader体系结构,并讨论了ClassLoaders对平台安全性和可扩展性的影响,以及实现用户定义的ClassLoader的方法。

由ClassLoader加载的最小执行单元是Java类文件。类文件包含Java类的二进制表示形式,该类具有可执行字节码和对该类使用的其他类的引用,包括对Java API中的类的引用。换句话说,ClassLoader定位需要加载的Java类的字节码,读取字节码,并创建java.lang.Class类的实例。以便JVM执行。当JVM启动时,不会加载任何内容。会首先加载正在执行的程序的类文件,然后加载其他正在执行的字节码中被引用的类和接口。因此,JVM表现出延迟加载特性,即仅在需要时加载类,在启动时,JVM不需要知道在运行时期间将加载的类。延迟加载在为Java平台提供动态可扩展性方面起着关键作用。通过在程序中实现自定义ClassLoader,可以自定义Java ClassLoader。

Java 2 类委托模型

Java 运行时中存在多个ClassLoaders实例,每个实例都从不同的代码库加载类。例如,Java核心API类由引导程序(或原始)ClassLoader加载。特定应用程序类由系统(或应用程序)ClassLoader加载。另外,应用程序可以定义自己的ClassLoader以从自定义库加载代码。Java 定义了ClassLoaders之间的父子关系。除引导程序ClassLoader之外的每个ClassLoader都有一个父类ClassLoader,从概念上形成了ClassLoader的树状结构。引导程序ClassLoader是此树的根,因此没有父级。这种关系如图所示。

以下是当客户端请求加载类时由ClassLoader执行的高级类加载算法:

  1. 执行检查以查看当前ClassLoader是否已加载所请求的类。如果是,则返回加载的类并完成请求。JVM缓存由ClassLoader加载的所有类。之前由ClassLoader加载的类不会再次加载。
  2. 如果尚未加载类,则在当前ClassLoader尝试加载之前,请求被委托给父ClassLoader。这个委托可以一直到引导程序ClassLoader,之后不再进行进一步的委托。
  3. 如果父级无法加载到类,则当前的ClassLoader将尝试搜索所请求的类。每个ClassLoader都定义了搜索要加载的类的位置。例如,引导程序ClassLoader搜索sun.boot.class.path系统属性中指定的位置(目录和zip / jar文件)。系统ClassLoader在JVM开始执行时传入的类路径(设置为java.class.path系统属性)命令行变量指定的位置中搜索类。如果找到该类,则将其加载到系统中并返回,完成请求。
  4. 如果找不到该类,则抛出java.lang.ClassNotFoundException。

实现Java 2 自定义类加载器

除了引导程序ClassLoader,它是在JVM中的本机代码中实现的外,其他ClassLoader都是通过扩展java.lang.Class.Loader类来实现的。以下代码显示了Java 2 ClassLoader API的相关方法:

public abstract class ClassLoader extends Object {protected ClassLoader(ClassLoader parent) {}protected final Class defineClass( String name,byte[] b,int off,int len) throws ClassFormatError{}protected Class findClass(String className) throws ClassNotFoundException {}public Class loadClass(String className) throws ClassNotFoundException {}
}
复制代码

根据父委派模型,每个ClassLoader在创建时都会分配一个父级。客户端在ClassLoader的实例上调用loadClass方法来加载类。这启动了前面解释的类加载算法。在Java 2之前,java.lang.ClassLoader类中的loadClass方法被声明为abstract,在扩展java.lang.ClassLoader类时需要自定义ClassLoaders来实现它。实现loadClass方法相当复杂,因此在Java 2中已经改变了。随着ClassLoader父委托模型的引入,java.lang.ClassLoader有一个loadClass方法的实现,它本质上是一个执行类加载的模板方法算法。 loadClass方法在类加载算法的第3步中调用findClass方法(在Java 2中引入)。自定义类加载器应该重写此方法,以提供定位和加载Java类的自定义方法。这极大地简化了自定义ClassLoader的实现。

findClass方法调用loadFromCustomRepository来搜索存储库中的给定类,如果找到,则读取并返回该类的字节码。该类的原始字节码被传递到java.lang.ClassLoader类中实现的defineClass方法,该类返回java.lang.Class对象的实例。 这使得新类可用于正在运行的Java程序。defineClass方法还确保自定义ClassLoader不会通过从自定义存储库加载来重新定义核心Java API类。 如果传递给defineClass的类名以“java”开头,则抛出SecurityException。

应该注意的是,在启动时,JVM不需要知道传递给loadClass方法的字符串所代表的类。

与Java 2委派模型的偏差

Java 2委派模型并非适用于所有情况。在某些情况下,ClassLoader必须与Java 2模型不同。例如,servlet规范建议,实现Web应用程序ClassLoader,以便包含在Web应用程序归档中的类和资源优先于驻留在容器范围的JAR文件中的类和资源。为了满足此建议,Web应用程序ClassLoader应首先在其本地存储库中搜索类和资源,然后再委托父类ClassLoader,从而偏离Java 2委派模型。此建议使Web应用程序可以使用不同于servlet容器使用的类/资源版本。例如,可以使用较新版本的XML解析器中提供的功能而不是servlet容器使用的功能来实现Web应用程序。

可以通过覆盖java.lang.Classloader类的loadClass方法来实现满足servlet规范建议的Web应用程序ClassLoader。

应用程序加载器

ClassLoaders提供了一些可在Java程序中使用的强大功能。

热部署

在正在运行的应用程序中升级软件而不重新启动它称为热部署。对于Java应用程序,热部署意味着在运行时升级Java类。ClassLoaders在基于Java的应用程序服务器中发挥重要作用,以实现热部署。大多数基于Java的应用程序服务器(如EJB服务器和servlet容器)都使用此功能。ClassLoader无法重新加载已加载的类,但使用ClassLoader的新实例会将类重新加载到正在运行的程序中。

ClassLoader customLoader = new CustomClassLoader(repository);loadAndInvoke(customLoader,classToLoad);System.out.println("waiting.Hit Enter to continue");System.in.read();customLoader = new CustomClassLoader(repository);loadAndInvoke(customLoader,classToLoad);
复制代码

创建CustomClassLoader的实例以从指定为命令行参数的存储库加载类。loadAndInvoke加载一个类HelloWorld,它也被指定为命令行参数,并在其实例上调用一个方法,该方法在控制台上输出一条消息。当程序在第6行等待用户输入时,可以更改HelloWorld类(通过更改在控制台上打印的消息)并重新编译。 当程序继续执行时,在第7行创建一个新的CustomClassLoader实例。当loadAndInvoke执行第9行时,它会加载HelloWorld的更新版本,并在控制台上打印一条新消息。

修改类文件

ClassLoader在findClass方法中搜索类文件的字节码。找到字节码并将其读入程序后,可以在调用defineClass之前修改它们。例如,在调用defineClass之前,可能会将额外的调试信息添加到类文件中。某些安全应用程序的类文件数据可以加密存储在存储库中;findClass方法可以在调用defineClass之前解密数据。 程序可以动态生成字节码,而不是从存储库中检索它们。 这构成了JSP技术的基础。

类加载器与安全

由于ClassLoader负责将代码引入JVM,因此它的架构使得平台的安全性不会受到影响。 每个ClassLoader为它加载的类定义一个单独的命名空间,因此在运行时,一个类由其包名和加载它的ClassLoader唯一标识。一个类在其命名空间之外是不可见的;在运行时,在单独的命名空间中存在的类之间存在保护屏障。父委托模型使ClassLoader可以请求由其父级加载的类,因此ClassLoader不需要加载它所需的所有类。

Java运行时存在的各种类加载器具有不同可以从中加载代码的存储库。分离存储库位置可以将不同的信任级别分配给不同的存储库。由引导程序ClassLoader加载的Java运行时库在JVM中具有最高级别的信任。用户定义的ClassLoader的存储库具有较低的信任级别。此外,ClassLoaders可以将每个加载的类分配到保护域中。要根据系统安全策略(java.security.Policy的一个实例)定义代码权限,自定义ClassLoader应扩展java.security.SecureClassLoader类并调用其defineClass方法,该方法将java.security.CodeSource对象作为参数。SecureClassLoader的defineClass方法从系统策略中获取与CodeSource关联的权限,并基于此定义java.security.Protection域。有关安全模型的详细讨论超出了本文的范围。更多细节可以从Bill Venners的Inside the Java Virtual Machine一书中获得。

总结

ClassLoaders提供了一种强大的机制,通过它可以在运行时以有趣的方式扩展Java平台。 自定义类加载器可用于实现正常运行的Java程序可用的功能。 本文已讨论了其中一些应用程序。ClassLoaders在当前J2EE平台提供的一些技术中发挥着重要作用。有关Java类加载机制的更多详细信息,请阅读Java虚拟机内部。

转载于:https://juejin.im/post/5cbe65c26fb9a031fd633e62

Java ClassLoader详解相关推荐

  1. java classloader详解_Java类加载器(ClassLoader)详解

    本文主要讲述Java ClassLoader的工作原理,这为后面将Android App代码热替换或者插件化升级做铺垫 一. 类加载器 ClassLoader即常说的类加载器,其功能是用于从Class ...

  2. ClassLoader 详解及用途

    转载自 ClassLoader 详解及用途 ClassLoader主要对类的请求提供服务,当JVM需要某类时,它根据名称向ClassLoader要求这个类,然后由ClassLoader返回这个类的cl ...

  3. Apache Thrift - java开发详解

    2019独角兽企业重金招聘Python工程师标准>>> Apache Thrift - java开发详解 博客分类: java 架构 中间件 1.添加依赖 jar <depen ...

  4. Java泛型详解-史上讲解最详细的,没有之一

    目录 1. 概述 2. 一个栗子 3. 特性 4. 泛型的使用 4.1 泛型类 4.2 泛型接口 4.3 泛型通配符 4.4 泛型方法 4.4.1 泛型方法的基本用法 4.4.2 类中的泛型方法 4. ...

  5. Java虚拟机详解----JVM常见问题总结

    [正文] 声明:本文只是做一个总结,有关jvm的详细知识可以参考本人之前的系列文章,尤其是那篇:Java虚拟机详解04----GC算法和种类.那篇文章和本文是面试时的重点. 面试必问关键词:JVM垃圾 ...

  6. java 泛型详解、Java中的泛型方法、 java泛型详解

    本文参考java 泛型详解.Java中的泛型方法. java泛型详解 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用. 什么是泛型?为什么要使用泛型? 泛型,即& ...

  7. 最详细的java泛型详解

    来源:最详细的java泛型详解 对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考java 泛型详解.Java中的泛型方法. ja ...

  8. Java异常详解及如何处理

    来源:Java异常详解及如何处理 简介 程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常.异常发生时,是任程序自生自灭,立刻退出终止,还是输出错误给用户?或者用C语言 ...

  9. Java基础——Java NIO详解(一)

    一.基本概念 1.I/0简介 I/O即输入输出,是计算机与外界世界的一个借口.IO操作的实际主题是操作系统.在java编程中,一般使用流的方式来处理IO,所有的IO都被视作是单个字节的移动,通过str ...

最新文章

  1. 是否保证Python列表的元素保持按插入顺序排列?
  2. mysql合并到区间_合并区间
  3. 2018年5月5日论文阅读
  4. android view退出动画,android animation——view进来退出动画
  5. 半小时让你快速入门linux掌握基础命令
  6. [转载]高性能托管应用程序设计入门
  7. [最小割][Kruskal] Luogu P5039 最小生成树
  8. 密码生成的思路---电脑mac地址
  9. 解决办法:access restriction is not accessible due to restriction
  10. 红黑树 一张导图解决红黑树全部插入和删除问题 包含详细操作原理 情况对比
  11. 嵌入式软件与设计 学习笔记总结一
  12. 先将输入的一系列整数中的最小值与第一个数交换,然后将最大值与最后一个数交换,最后输出交换后的序列。
  13. CompletableFuture模拟复杂场景使用测试。。。
  14. PyCharm 4.5 激活码
  15. 【20210910】【机器/深度学习】lightGBM模型训练中报错:“Cannot set reference after freed raw data“
  16. MG本地服务器如何改ip
  17. Hessian矩阵的几何意义
  18. 数据结构C语言 胡学钢 PDF,数据结构(C语言版) 胡学钢.ppt
  19. c++商店信息管理系统的设计与实现
  20. 团队协作让你在职场中轻松获得成功

热门文章

  1. c++ primer plus 学习笔记
  2. Mysql binlog日志及binlog恢复数据库操作
  3. linux 集群配置ssh无密码访问
  4. 小程序页面之间的通讯利器 - nsevent
  5. jdbcTemplate 调用存储过程。 入参 array 返回 cursor
  6. IDEA坑爹跟新的小BUG解决之道
  7. 开启AngularJS学习之旅
  8. wget 命令用法详解
  9. 1_Linux基础知识及命令————登陆登出及图形化界面的初始化
  10. windows环境下跑hadoop自带的wordcount遇到的问题