`

Java中的ClassLoader(转)

    博客分类:
  • Java
 
阅读更多

转自:http://blog.csdn.net/ymeng_bupt/article/details/6843998

 

1.  Java中类的加载过程(如Dog类):

1.       通过类型信息定位Dog.class文件。

2.       载入Dog.class文件,创建相应的Class对象。

3.       执行父类的静态字段定义时初始化语句和父类的静态初始化块。

4.       执行子类的静态字段定义时初始化语句和子类的静态初始化块。

5.       当使用new Dog()方式时,在堆上为Dog对象分配存储空间,并清零分配的存储空间。

6.       执行父类的字段定义时初始化语句和父类的构造函数。

7.       执行子类的字段定义时初始化语句和子类的构造函数。

(参考Java编程思想,部分是自己测试的结果,还没有看到相关资料,这个顺序和C#的顺序好像有比较大的差别)

 

我们知道每个Java类编译后会生成一个.class文件,里面除了存储了类的字节码之外,还存储了和该类对应的Class对象的信息(包含一些用于反射的信息)。在JVM中,通过类加载器(ClassLoader)来实现Class对象的创建。

 

在Object类中有public的getClass()方法,我们可以通过该方法获取与之对应的Class对象。在获取类对应的Class对象后,我们就可以通过newInstance()方法或者通过获取其Constructor的方法来创建该类的实例,也可以通过Class对象获取类或实例相关的更多信息,如方法,字段等,即反射的功能。另外我们也可以通过Class.forName()方法,通过传入类字符串的方式来创建类的实例。(通过Class对象创建类实例和通过forName方法创建类实例是否有联系呢?这两种创建类的实例和直接通过new创建类实例是否也有联系呢?)

 

然而Dog.class文件是如何定位的?Dog.class文件是如何被加载并解析的?Class对象又是如何生成的呢?这就是ClassLoader需要解决的问题,也是这篇文章中接下来要讲述的。

2. ClassLoader的工作原理


在Java内部实现了三种类型的ClassLoader:Bootstrap ClassLoader,Extension ClassLoader, System ClassLoader。这三个ClassLoader通过parent形成一条单向链,其中Bootstrap处于链尾,而System ClassLoader处于链头。

 

Bootstrap ClassLoader

Bootstrap ClassLoader用于在启动JVM时加载类,以使JVM能正常工作,因而它是用Native代码实现的,最早被创建出来,处于最底层。

Bootstrap ClassLoader将搜索Java核心库(%JAVA_HOME%\jre\lib),如rt.jar、i18n.jar等,在这些核心库中包含了Java的核心类,即Bootstrap ClassLoader用于加载Java的核心类,包括java.lang、java.io等包中的类。

 

Extension ClassLoader

Extension ClassLoader位于链的中间层,它将搜索特定的标准扩展目录。该标准扩展目录在不同的JVM实现中不一定相同,在sun的JVM中,它是%JRE_HOME% \lib\ext,具体路径可以通过java.ext.dirs系统属性值获取。该标准扩展目录存在的目的在于扩展和共享,应用程序厂商可以将部分共享库放置于此,而不是各自程序的目录下的多份拷贝。在开发过程中,我们也可以把部分常用的库放置于此,而不必每次都去配置环境。

 

System ClassLoader

System ClassLoader位于链的最顶层,它将搜索CLASSPATH中配置的目录和jar文件。

 

3. 三种ClassLoader的协同工作

既然系统中有三种不同的ClassLoader,那么一个类的加载是用那个ClassLoader呢?在Java中,是通过“代理模型(delegation model)”来决定使用哪个ClassLoader来加载类的(读取.class文件,并创建相应的Class对象)。当一个类需要被加载的时候,系统默认通过System ClassLoader来加载该类;然而System ClassLoader并不立即加载该类,它会将加载的行为代理给其parent去加载,只有当其parent不能加载该类的时,System ClassLoader才去搜索CLASSPATH中的目录和jar文件以加载该类,若找到对应的.class文件,则加载该类,否则抛出ClassNotFoundException。当它将加载的行为代理给Extension ClassLoader并最终代理给Bootstrap ClassLoader的时候,他们也做的是相似的事情。从设计的角度,这其实就是一种职责链模式(Chain of Responsibility Pattern)

那么Java为什么需要这种代理模型呢?答案是出于安全的考虑。Java是一种安全的编程语言,它会对每个加载的类执行安全检查,但是对Java核心库中的类是不执行安全检查的(即他们是受JVM信赖的)。(什么是安全检查?以我现在所知,就是用户可以通过security manager来控制特定目录的访问权限,如何使用这些控制将会是另一个主题。对安全检查是否还有其他的呢?)那么假如用户可以加载自己编写的和核心库同名的类(如java.lang.Object),那么这些用户编写的类就可以绕过安全检查,从而为一些恶意用户提供了一种破坏的途径。然而在使用这种代理模型后,类的加载首先会代理到Bootstrap ClassLoader中实行加载,如果它发现当前核心类库中可以加载对应的类,系统会加载核心库中的类,而不会加载用户编写的类。

 

但是如果只是这样,还不足以保证类的加载的安全。因为用户完全可以定义自己的ClassLoader,在自己的ClassLoader中破坏这种代理模型,那么用户自己写的java.lang.Object就可以被加载了。为了解决这个问题,在Java中认为两个类名相同的实例,如果加载他们的ClassLoader不同,那么他们是不同的类型,即他们之间不能转换,而且用instanceof操作符返回的是false。这样的话,即使用户加载了自己的java.lang.Object类,它也不是系统认为可以信赖的java.lang.Object,那么它就绕不过安全检查机制。(以上的描述只是我的猜想,我还没有看到相关的资料提到该问题,因而有待验证。)

 

有人说使用这种机制可以实现兼容性的升级,在软件的旧版本中使用某个旧版本的类,在升级的某些新部分中可以使用相同类的新版本,即实现内存中保留一个类的两个版本。我认为这种不应该是一种推荐的做法,在内存中保留一个类的两个版本极易在运行是引起一些莫名其妙的错误。

 

很可惜,我没能找到以上三种ClassLoader的源码,看不了内部的实现。在Java中,它们都是通过ClassLoader这个抽象类来表示的。以下简单的介绍一下ClassLoader内部的成员

(其中Extension ClassLoader的实现类为:sun .misc.Launcher$ExtClassLoader)

 

4. ClassLoader类介绍

ClassLoader类位于java.lang包中,直接继承自Object,是一个抽象类。

 

两个构造函数(protected)

一个带parent的参数,一个无参(默认用System ClassLoader作为其parent)。

 

静态方法

public static ClassLoader getSystemClassLoader()

获取System ClassLoader。可以通过设置系统属性:java.system.class.loader的值以修改该函数的返回类型(System.setProperty, System.getProperty)。

 

public static URL getSystemResource(String name)

Resource是指数据,如图片、文本、音频等。我的理解Resource就是指在对应ClassLoader搜索路径(目录和jar文件内部)下的任何数据,包括文件和目录。那么该函数就是通过名字(目录名或文件名)返回资源所对应的URL值。(注:名字和返回中的目录分隔符都是用“/”来表示)。该函数的搜索顺序和.class文件的搜索顺序是一样的,所不同的是,用户自定义的ClassLoader可以通过重写public URL findResource(String name)方法来扩展该函数的搜索范围。

 

public static Enumeration<URL> getSystemResources(String name)

同getSystemResource,只是它会返回多个URL的集合,用户自定义的ClassLoader可以通过public URL findResources(String name)来扩展该方法。

 

public static InputStream getSystemResourceAsStream(String name)

该函数内部通过调用getSystemResource()方法获取资源的URL地址,然后调用URL.openStream()方法获取资源的InputStream。

 

public方法

public Class<?> loadClass(String name) throws ClassNotFoundException

protected synchronized Class<?> loadClass(String name,boolean resolve)

    throws ClassNotFoundException

通过类名加载类,在ClassLoader中,会缓存已加载的Class实例,因而该方法首先查询缓存中是否已经存在该类的Class实例。该方法实现了ClassLoader加载类的算法。按JDK的表述,resolve是指链接(link)的意思,但是link又是指什么呢?这个我还清楚。用户自定义子类可以通过findClass()方法来扩展该方法。

 

public URL getResource(String name)

类似静态方法的getResource()方法。

 

public Enumeration<URL> getResources(String name)

类似静态方法的getResources()方法。

 

public final ClassLoader getParent()

如方法名。

 

public InputStream getResourceAsStream(String name)

类似静态方法的getResourceAsStream()方法

 

protected方法

protected URL findResource(String name)

getResource()的扩展方法,提供给子类重写。

 

protected Enumeration<URL> findResources(String name) throws IOException

getResources()的扩展方法,提供给子类重写。

 

protected Class<?> findClass(String name) throws ClassNotFoundException

loadClass()函数的扩展方法,该方法会在所有parent ClassLoader没有找到相应的类定义的时候调用,因而该方法只需要实现当前ClassLoader中需要额外搜索的路径即可。

 

protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,

       ProtectionDomain protectionDomain) throws ClassFormatError

protected final Class<?> defineClass(String name,byte[] b,int off, int len, ProtectionDomain protectionDomain) throws ClassFormatError

protected final Class<?> defineClass(String name,byte[] b,int off, int len) throws ClassFormatError

该方法用于将字节数组转化为相应的Class对象,在该Class对象使用前,该对象必须已经被解析了(resolved or linked,同样,什么是解析或链接?)。可以指定ProtectionDomain,也可以使用默认的ProtectionDomain。子类在实现findClass()的时候可以调用该方法以将找到的.class二进制内容转化为Class实例。

5. 自定义类加载器

[java] view plaincopy
 
  1. package classloader;  
  2.   
  3. import java.io.ByteArrayOutputStream;  
  4. import java.io.File;  
  5. import java.io.FileInputStream;  
  6.   
  7. /** 
  8.  *  
  9.  * 一、ClassLoader加载类的顺序 
  10.  *  1.调用 findLoadedClass(String) 来检查是否已经加载类。 
  11.  *  2.在父类加载器上调用 loadClass 方法。如果父类加载器为 null,则使用虚拟机的内置类加载器。  
  12.  *  3.调用 findClass(String) 方法查找类。 
  13.  * 二、实现自己的类加载器 
  14.  *  1.获取类的class文件的字节数组 
  15.  *  2.将字节数组转换为Class类的实例 
  16.  *  
  17.  *  
  18.  * @author lei 2011-9-1 
  19.  */  
  20. public class ClassLoaderTest {  
  21.   
  22.     public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {  
  23.         //新建一个类加载器  
  24.         MyClassLoader cl = new MyClassLoader("myClassLoader");  
  25.         //加载类,得到Class对象  
  26.         Class<?> clazz = cl.loadClass("classloader.Animal");  
  27.         //得到类的实例  
  28.         Animal animal=(Animal) clazz.newInstance();  
  29.         animal.say();  
  30.     }  
  31.   
  32. }  
  33. class Animal{  
  34.     public void say(){  
  35.         System.out.println("hello world!");  
  36.     }  
  37. }  
  38. class MyClassLoader extends ClassLoader {  
  39.     //类加载器的名称  
  40.     private String name;  
  41.     //类存放的路径  
  42.     private String path = "E:\\workspace\\Algorithm\\src";  
  43.   
  44.     MyClassLoader(String name) {  
  45.         this.name = name;  
  46.     }  
  47.       
  48.     MyClassLoader(ClassLoader parent, String name) {  
  49.         super(parent);  
  50.         this.name = name;  
  51.     }  
  52.     /** 
  53.      * 重写findClass方法 
  54.      */  
  55.     @Override  
  56.     public Class<?> findClass(String name) {  
  57.         byte[] data = loadClassData(name);  
  58.         return this.defineClass(name, data, 0, data.length);  
  59.     }  
  60.     public byte[] loadClassData(String name) {  
  61.         try {  
  62.             name = name.replace(".""//");  
  63.             FileInputStream is = new FileInputStream(new File(path + name + ".class"));  
  64.             ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  65.             int b = 0;  
  66.             while ((b = is.read()) != -1) {  
  67.                 baos.write(b);  
  68.             }  
  69.             return baos.toByteArray();  
  70.         } catch (Exception e) {  
  71.             e.printStackTrace();  
  72.         }  
  73.         return null;  
  74.     }  
  75.   
  76. }
  77.  
分享到:
评论

相关推荐

    java中classLoader的使用

    Java中的类加载器(ClassLoader)是Java虚拟机(JVM)的一个重要组成部分,它负责将类的.class文件从文件系统或者网络中加载到内存中,并转换为对应的Class对象。类加载器的工作流程主要包括加载、验证、准备、解析...

    java classloader classpath 张孝祥

    类加载器(`ClassLoader`)是Java运行时环境的一部分,它的主要职责是从文件系统或网络中获取字节码,将其转换为`Class`对象,并在Java虚拟机中运行。Java中的类加载机制遵循“按需加载”原则,即只有当程序真正需要...

    破解java加密的ClassLoader.java,在classloader植入破解代码

    破解java加密的ClassLoader.java,在classloader植入破解代码

    Java ClassLoader定制实例

    在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中。理解ClassLoader的工作原理以及如何定制它,对于深入学习Java的运行机制和进行高级应用开发具有重要意义。本篇文章将...

    java中ClassLoader

    Java 中的 ClassLoader Java 中的 ClassLoader 是一个非常重要的组件,它负责动态加载 class 文件到虚拟机当中,并将其转换成 java.lang.Class 类的一个实例。每个这样的实例用来表示一个 Java 类,因此我们可以...

    java classloader

    Java ClassLoader是一个核心的Java运行时组件,负责加载类到Java虚拟机(JVM)中。它是Java平台的独特特性,因为它允许动态加载类,增强了软件的可扩展性和灵活性。这篇博文(虽然链接不可用)可能深入探讨了...

    java ClassLoader机制及其在OSGi中的应用

    Java ClassLoader机制是Java虚拟机(JVM)中一个至关重要的组成部分,它的主要任务是将类的.class文件加载到JVM中,使得程序能够运行。ClassLoader不仅负责类的加载,还涉及类的验证、初始化等一系列过程。理解...

    探究java的ClassLoader及类变量初始化顺序

    在Java编程语言中,ClassLoader是核心组件之一,它负责加载类到JVM(Java虚拟机)中。理解ClassLoader的工作机制以及类变量初始化的顺序对于深入理解Java运行时环境至关重要。这篇博文将探讨这两个主题。 首先,让...

    测试普通Java程序ClassLoader等级关系的Demo程序

    本Demo程序主要探讨的是Java中的ClassLoader等级关系,这一主题对于理解Java应用程序的运行机制至关重要。 首先,我们要了解ClassLoader的基本概念。在Java中,ClassLoader是一个系统组件,它根据指定的全限定类名...

    Java_ClassLoader详解

    在 Java 语言中,类加载器(ClassLoader)是 Java 运行时环境的核心组成部分之一,它负责将编译后的 `.class` 文件加载到 JVM 中执行。从 JDK 1.0 开始,随着 Java Applet 的出现以及网络应用的需求增加,类加载机制...

    理解Java ClassLoader机制

    Java ClassLoader机制是Java运行时环境中的核心组件之一,它负责加载类到JVM(Java虚拟机)中,使得程序能够执行。理解ClassLoader的工作原理对于优化应用性能、处理类加载问题以及实现自定义加载器至关重要。 首先...

    Java ClassLoader原理

    ### Java ClassLoader原理详解 #### 摘要 本文探讨了Java虚拟机(JVM)中的一个重要特性:动态类加载(Dynamic Class Loading)。这一机制为Java平台提供了强大的能力,允许在运行时安装软件组件,例如从网络下载...

    Java ClassLoader学习总结

    ClassLoader 是 Java 中的一个抽象类,它的主要作用是加载 Class 文件到 JVM 中。ClassLoader 使用了双亲委托模式进行类加载,每一个自定义的 ClassLoader 都必须继承 ClassLoader 这个抽象类,而每个 ClassLoader ...

    Java实现热加载完整代码;Java动态加载class;Java覆盖已加载的class;Java自定义classloader

    让Java支持热加载是个不错的想法。如何做到的呢? 1. 定义好接口和实现类 2. 让代理类通过反射的方式调用实现类,对外暴露的是代理类。 3. 自定义URLClassLoader。检查实现类.class文件的...Java自定义classloader;

    java的ClassLoader类加载器机制

    在 Java 虚拟机(JVM)中,类加载器(ClassLoader)扮演着非常重要的角色。类加载器负责加载 Java 类,包括核心类和用户自定义类。在 JVM 运行过程中,类加载器会形成一个层次结构,包括引导类加载器(Bootstrap ...

    ClassLoader

    ### Java虚拟机中ClassLoader概述与双亲委托机制详解 #### 一、ClassLoader概念与作用 在Java编程语言中,`ClassLoader`是一个非常重要的组件,它负责加载程序运行所需的类文件到Java虚拟机(JVM)中。`ClassLoader`...

    Understanding the Java ClassLoader

    - **Extension ClassLoader**:这个ClassLoader加载Java扩展包中的类。 - **System/App ClassLoader**:这个ClassLoader加载应用程序类路径中的类。 #### 三、自定义ClassLoader实践 为了更深入地理解ClassLoader...

Global site tag (gtag.js) - Google Analytics