`
lclcr
  • 浏览: 125595 次
  • 性别: Icon_minigender_1
  • 来自: 山东
社区版块
存档分类
最新评论

ClassLoader

    博客分类:
  • JAVA
阅读更多

      当运行Java程序时,首先运行JVM(java虚拟机),然后再把java class加载到JVM里头运行。类装载就是寻找一个类或接口的字节码文件并通过解析该字节码来构造代表这个类或接口的class对象的过程。类装载器把一个类装入Java虚拟机要经过三个步骤成:装载、链接和初始化,其中链接又可以分成校验、准备和解析三步,除了解析外,其它步骤是严格按照顺序完成的,各个步骤的主要工作如下:

1. 装载:查找和导入类或接口的字节码; 
2. 链接:执行校验、准备和解析,其中解析是可选的; 
	校验:检查导入的类或接口的二进制数据的正确性; 
	准备:给类的静态变量分配并初始化存储空间; 
	解析:将符号引用转成直接引用; 
3. 初始化:激活类的静态变量的初始化Java代码和静态Java代码块。 

     负责加载java class的这部分就叫做类加载器(Class Loader)。
      一旦被引用的类型被装载了,虚拟机仔细检查它的二进制数据。如果类型是一个类,并且不是java.lang.Object,虚拟机根据类的数据得到它的超类的全限定名。虚拟机接着会查看超类是否已经被装载。如果没有,先装载超类,然后虚拟机再次检查它的二进制数据来找它的超类。一直重复到超类为Object为止。
      当一个类被虚拟机装载后,通过递归,所有该类的超类和超接口也都被成功装载了,虚拟机就会创建新的Class实例来代表这个类。如果定义类型的字节是由用户自定义的类装载器确定或者生成,然后传递到difineClass()方法,defineClass()方法会在这个时候返回这个新的Class实例。或者用户自定义的类装载器通过findSystemClass()调用委派启动类装载器来装载,findSystemClass()会在这个时候返回Class实例。直到返回了Class实例,loadClass()方法才会返回这个Class实例给调用者。

      JVM中默认的三个ClassLoader为:

Bootstrap ClassLoader、Extension ClassLoader和App  ClassLoader。

      Bootstrap是用C++编写的,是JVM自带的类装载器,由JVM启动,用来装载核心类库(JRE\lib\rt.jar、sunrsasign.jar、charsets.jar等)。App ClassLoader和Ext ClassLoader是用Java语言编写的,由Bootstrap ClassLoader加载。App ClassLoader的Parent是Extension ClassLoader;Extension ClassLoader的Parent为Bootstrap ClassLoader。Extension ClassLoader负责加载扩展的Java class(例如所有javax.*开头的类和存放在JRE的ext目录下的类),App ClassLoader负责加载CLASSPATH指定路径下的类。

public class TestLoaderExtends {
	public static void main(String[] args) {
		Class<?> c = TestLoaderExtends.class;
		ClassLoader loader = c.getClassLoader();
		System.out.println(loader);		//sun.misc.Launcher$AppClassLoader@19821f
		System.out.println(loader.getParent());	//sun.misc.Launcher$ExtClassLoader@addbf1
		System.out.println(loader.getParent().getParent());	//null
	}
}

      Java应用程序从某个类的main()方法开始执行,其他的类在需要的时候才动态连接,如果一个类在所有操作中都没有被用到,这个类就不会被加载。java动态载入class的两种方式为初始化的时候隐式装入和通过java.lang.Class的forName()方法或java.lang.ClassLoader的loadClass()方法显示装入。 

public static Class<?> forName(String className) 	
	从装载当前对象实例所在的ClassLoader中装载类,其中的className为类的全限定名。调用此方法等效于: 
	Class.forName(className, true, currentLoader)
	其中 currentLoader 表示当前类的定义类加载器。

 

public static Class<?> forName(String name, boolean initialize,ClassLoader loader)

      如果initialize参数为true,类型会在forName()方法返回之前连接并初始化;如果initialize为false,类型会被装载,可能会被连接但是不会被forName()方法明确地初始化。然而,如果该类型在调用forName()之前已经被初始化了,及时initialize的值为false,返回的类型也已经被初始化了。

Class<?> loadClass(String name)  使用指定的二进制名称来加载类。

       ClassLoader的实例(MyClassLoader)调用loadClass方法,是指从当前ClassLoader(MyClassLoader)实例中调用类(A),而这个实例与装载当前所在类实例(TestClient)的Classloader(AppClassLoader)也许不是同一个. 
       例如用户定义了ClassLoader的子类MyClassLoader。TestClient.class通过AppClassLoader装载。当在TestClient的某个方法中使用MyClassLoader的loadClass方法来装入某个类A时,类A是通过MyClassLoader装载的,而TestClient实例的ClassLoader是AppClassLoader,两者不是同一个。
      当我们希望在装入一个class文件的时候做一些特定的初始化工作时,可以通过这种自定义类加载器的方式,调用loadClass()方法来实现。

      Java提供了抽象类ClassLoader,所有用户自定义类装载器都实例化自ClassLoader的子类。 

public abstract class ClassLoader extends Object 

      ClassLoader 类是一个抽象类。每个Class对象都包含一个对定义它的ClassLoader的引用。System Class Loader是一个特殊的用户自定义类装载器,由JVM的实现者提供,在编程者不特别指定装载器的情况下默认装载用户类。系统类装载器可以通过ClassLoader.getSystemClassLoader()方法得到。
      然而,有些类可能并非源自一个文件;它们可能源自其他来源(如网络),也可能是由应用程序构造的。defineClass方法将一个byte数组转换为Class类的实例。这种新定义的类的实例可以使用Class.newInstance来创建。 
      例如,应用程序可以创建一个网络类加载器,从服务器中下载类文件。示例代码如下所示:  

ClassLoader loader = new NetworkClassLoader(host, port);
Object main = loader.loadClass("Main", true).newInstance();

      网络类加载器子类必须定义方法 findClass 和 loadClassData,以实现从网络加载类。下载组成该类的字节后,它应该使用方法defineClass来创建类实例。示例实现如下(详见类OnePointOneClassLoader): 

class NetworkClassLoader extends ClassLoader {
	String host;
	int port;
	public Class findClass(String name) {
		byte[] b = loadClassData(name);
		return defineClass(name, b, 0, b.length);
	}
	private byte[] loadClassData(String name) {
		// load the class data from the connection
		. . .
	}
}

      ClassLoader类的构造方法 

protected  ClassLoader() 
	  使用方法 getSystemClassLoader() 返回的 ClassLoader 创建一个新的类加载器,将该加载器作为父类加载器。 
protected  ClassLoader(ClassLoader parent) 
	  使用指定的、用于委托操作的父类加载器创建新的类加载器。 

      ClassLoader类的常用方法 

(1).loadClass
		Class<?> loadClass(String name) 使用指定的二进制名称来加载类。  
		protected  Class<?> loadClass(String name, boolean resolve) 使用指定的二进制名称来加载类。 
name参数指定了JVM需要的类的名称,该名称为类的全县定名;resolve参数表示是否分析这个类。在初始化类之前,应考虑类分析,并不是所有的类都需要分析,如果JVM只需要知道该类是否存在或找出该类的超类,那么就不需要分析。这个方法是ClassLoader的入口点。
		此方法的默认实现将按以下顺序搜索类:
		a.调用findLoadedClass(String)来检查是否已经加载类。
		b.在父类加载器上调用loadClass方法。如果父类加载器为null,则使用虚拟机的内置类加载器。 
		c.调用findClass(String)方法查找类。
(2).defineClass
		protected  Class<?> defineClass(String name, byte[] b, int off, int len) 将一个byte数组转换为Class类的实例。 
这个方法接受类文件的字节数组并把它转换成Class对象。字节数组可以是从本地文件系统或网络装入的数据。它把字节码分析成运行时数据结构、校验有效性等等。
(3).findClass  
		protected  Class<?> findClass(String name) 使用指定的二进制名称查找类。
		protected  Class<?> findSystemClass(String name)
			此方法通过系统类加载(getSystemClassLoader())来加载该类。返回的 Class 对象具有多个与之相关联的 ClassLoader。
		protected  Class<?>findLoadedClass(String name)
        			如果 Java 虚拟机已将此加载器记录为具有给定二进制名称的某个类的启动加载器,则返回该二进制名称的类。
(4).resolveClass		
		protected voidresolveClass(Classc)方法解析装入的类
		如果该类已经被解析过那么将不做处理。当调用loadClass方法时,通过它的resolve 参数决定是否要进行解析。

      在1.2版本之前自定义类加载器必须创建java.lang.ClassLoader的子类并实现loadClass(),loadClass()方法的工作方式:

           (1).查看请求的类型是否已经被装载了(findLoadedClass()),如果是直接返回这个已经被装载的类型的Class实例。
           (2).否则,委派到这个类加载器的双亲加载器,如果一个双亲返回了一个Class实例,则将该实例直接返回。
           (3).否则,调用findClass()。findClass()会试图寻找或者生成一个字节数组,内容采用Java class格式。如果成功,findClass()将这个字节传递个difineClass()。defineClass()将导入该字节数组,并返回一个Class实例。如果findClass()返回了一个Class实例,loadClass()就把这个Class实例返回。
           (4).否则,findClass()抛出某些异常来终止处理,loadClass()抛出同样的异常并终止。      

package cloader;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import com.hansky.core.io.ByteOutputStream;

public class OnePointOneClassLoader extends ClassLoader {
	
	private String basePath ;
	public OnePointOneClassLoader (String basePath) {
		this.basePath = basePath;
	}
	
	@Override
	public synchronized Class<?> loadClass(String className) throws ClassNotFoundException {
		return this.loadClass(className, true);
	}
	
	@Override
	public synchronized Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
		Class<?> c = findLoadedClass(className);	//判断当前类是否已经被加载	
		if (null != c) {
			System.out.println(className + " has been loaded .");
			return c;
		}
		try{
			c = super.findSystemClass(className);
			return c;
		} catch (ClassNotFoundException e) {			
		}
		byte [] bytes = getByteArrayFromBasePath(className);
		if (null == bytes) {
			System.out.println("Class format error " + className);
			return null;
		}
		c = defineClass(className, bytes, 0, bytes.length);
		if (null == c) {
			throw new ClassFormatError("Class Format error !");
		}
		if (resolve) {
			resolveClass(c);
		}
		return c;
	}	

	private byte [] getByteArrayFromBasePath (String name) {
		String fileAbsPath = this.basePath + File.separatorChar + name.replace('.', File.separatorChar) + ".class";
		FileInputStream fis = null;
		try {
			fis = new FileInputStream(fileAbsPath);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		BufferedInputStream bis = new BufferedInputStream(fis);
		ByteOutputStream bos = new ByteOutputStream();
		int c = -1;
		try {
			while ((c = bis.read())!= -1) {
				bos.write(c);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return bos.toByteArray();
	}
	
	public static void main(String[] args) {
		OnePointOneClassLoader loader = new OnePointOneClassLoader("d:\\test");
		Class<?> c = null;
		try {
			c = loader.loadClass("cloader.Hello");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		try {
			IGreet greet = (IGreet) c.newInstance();
			greet.sayHello();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}
}

      虽然在版本1.2中仍然可以生成ClassLoader的子类并覆盖loadClass()方法,但是建议采用生成ClassLoader的子类并实现findClass()方法的方式。findClass()方法的工作方式为:
            (1).接受一个全限定名作为唯一参数,试图查找或生成一个字节数组,内容是Java class文件格式。如果findClass()无法确定或者生成字节数组,它抛出ClassNotFoundException异常并终止。
            (2).否则,findClass()调用defineClass(),把所需要的类型名称、字节数组等传递给defineClass()方法。如果defineClass()返回了一个代表这个类型的Class实例,findClass()简单地将这个Class实例返回给调用者。
            (3).否则,fineClass()抛出某些异常来终止处理,findClass()抛出同样的异常并终止。

package cloader;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import com.hansky.core.io.ByteOutputStream;

public class OnePointTwoClassLoader extends ClassLoader {
	private String basePath ;
	
	public OnePointTwoClassLoader (String basePath) {
		this.basePath = basePath;
	}
	
	@Override
	protected Class<?> findClass(String className) throws ClassNotFoundException {
		byte [] bytes = getByteArrayFromBasePath(className);
		if (null == bytes) {
			throw new ClassNotFoundException();
		}
		return defineClass(className, bytes, 0, bytes.length);
	}
	
	private byte [] getByteArrayFromBasePath (String className) {
		//同上
	}
	
	public static void main(String[] args) {
		//同上
	}

}

 

分享到:
评论

相关推荐

    ClassLoader运行机制 自己写的

    在Java虚拟机(JVM)中,类加载器(ClassLoader)是至关重要的组成部分,它负责查找和加载类的字节码文件。理解ClassLoader的工作机制对于深入掌握Java应用程序的运行至关重要。这里我们将详细讨论ClassLoader的运行...

    深入理解ClassLoader工作机制.docx

    《深入理解ClassLoader工作机制》 Java虚拟机(JVM)中的ClassLoader是负责加载类到内存中的核心组件。它不仅承担着将字节码转换为可执行对象的重任,还参与了类生命周期的各个阶段,包括加载、验证、准备、解析、...

    自定义classloader的使用

    在Java中,Classloader是加载类的关键组件,它负责查找、加载和初始化字节码文件。自定义Classloader允许开发者根据特定需求定制类的加载逻辑,例如加密类文件、隔离不同版本的库或者动态加载代码。本文将深入探讨...

    ClassLoader小例子

    在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中,使得程序能够执行。本示例"ClassLoader小例子"将深入探讨这个概念,并通过一个具体的程序来演示其工作原理。下面我们...

    classloader

    Java ClassLoader是Java运行时系统的关键但经常被忽视的组件,负责在运行时查找和加载类文件。通过创建自定义ClassLoader,你可以定制JVM,使类文件的引入方式完全重新定义,这提供了很多实用和有趣的可能。这篇教程...

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

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

    classloader 加密解密应用程序 ,反编译class

    在Java编程语言中,`ClassLoader`是一个至关重要的组件,它负责加载类到JVM(Java虚拟机)中。本文将深入探讨`ClassLoader`的工作原理、加密解密应用程序以及如何防止类被反编译。 首先,让我们理解`ClassLoader`的...

    Java ClassLoader定制实例

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

    lassLoader的关系以及如何防止ClassLoader内存泄漏

    在Java世界中,类加载器(ClassLoader)是关键的组件之一,它负责将类的字节码文件(.class)从文件系统或网络中加载到Java虚拟机(JVM)中,使得程序能够运行。本篇文章将深入探讨ClassLoader的关系网络以及如何...

    ClassLoader 详解.doc

    《ClassLoader详解》 Java应用程序的运行离不开类的加载,而ClassLoader正是这个过程的关键角色。它负责将类的字节码加载到Java虚拟机(JVM)中并转换为可执行的Java对象。深入理解ClassLoader的工作原理对于优化...

    理解Java ClassLoader机制

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

    ClassLoader类加载机制和原理详解

    在Java编程语言中,ClassLoader是核心组件之一,它负责加载类到JVM(Java虚拟机)中执行。本文将深入探讨ClassLoader的工作原理和类加载机制,帮助开发者理解这个至关重要的概念。 1. 类加载机制概述 Java的类加载...

    ClassLoader的 一些测试

    在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中执行。这篇测试主要探讨了ClassLoader的工作原理及其在实际应用中的使用。通过阅读给出的博文链接,我们可以深入理解...

    java classloader classpath 张孝祥

    ### Java ClassLoader与ClassPath详解 #### 一、概述 在Java编程中,类加载机制是十分关键的一个环节。类加载器(`ClassLoader`)负责将编译后的`.class`文件加载到Java虚拟机(JVM)中执行,而类路径(`ClassPath...

    classloader体系结构(含hotswap)

    Java的类加载器(ClassLoader)体系结构是JVM(Java虚拟机)中至关重要的一部分,它负责将类的字节码转换为运行时的类实例。本文将深入探讨启动类加载器、扩展类加载器、系统类加载器以及用户自定义类加载器,同时还...

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

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

Global site tag (gtag.js) - Google Analytics