翻译原文连接html
Java平台旨在提供健壮,安全和可扩展的功能,以支持代码和数据的移动性。Java虚拟机(JVM)中的Java ClassLoader是实现这些目标的关键组件。java
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 运行时中存在多个ClassLoaders实例,每一个实例都从不一样的代码库加载类。例如,Java核心API类由引导程序(或原始)ClassLoader加载。特定应用程序类由系统(或应用程序)ClassLoader加载。另外,应用程序能够定义本身的ClassLoader以从自定义库加载代码。Java 定义了ClassLoaders之间的父子关系。除引导程序ClassLoader以外的每一个ClassLoader都有一个父类ClassLoader,从概念上造成了ClassLoader的树状结构。引导程序ClassLoader是此树的根,所以没有父级。这种关系如图所示。 bash
如下是当客户端请求加载类时由ClassLoader执行的高级类加载算法:服务器
除了引导程序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的实现。ide
findClass方法调用loadFromCustomRepository来搜索存储库中的给定类,若是找到,则读取并返回该类的字节码。该类的原始字节码被传递到java.lang.ClassLoader类中实现的defineClass方法,该类返回java.lang.Class对象的实例。 这使得新类可用于正在运行的Java程序。defineClass方法还确保自定义ClassLoader不会经过从自定义存储库加载来从新定义核心Java API类。 若是传递给defineClass的类名以“java”开头,则抛出SecurityException。加密
应该注意的是,在启动时,JVM不须要知道传递给loadClass方法的字符串所表明的类。
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虚拟机内部。