类加载器





类加载器基本概念

顾名思义,类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java 。通过此实例的 newInstance()方法就可以创建出该类的一个对象。实际的情况可能更加复杂,比如 Java 字节代码可能是通过工具动态生成的,也可能是通过网络下载的。

基本上所有的类加载器都是 java.lang.ClassLoader类的一个实例。下面详细介绍这个 Java 类。

java.lang.ClassLoader类介绍

java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个 Java 类,即 java.lang.Class类的一个实例。除此之外,ClassLoader还负责加载 Java 应用所需的资源,如图像文件和配置文件等。不过本文只讨论其加载类的功能。为了完成加载类的这个职责,ClassLoader提供了一系列的方法,比较重要的方法如 表 1所示。关于这些方法的细节会在下面进行介绍。


表 1. ClassLoader 中与加载类相关的方法

方法

说明

getParent()

返回该类加载器的父类加载器。

loadClass(String name)

加载名称为 name的类,返回的结果是 java.lang.Class类的实例。

findClass(String name)

查找名称为 name的类,返回的结果是 java.lang.Class类的实例。

findLoadedClass(String name)

查找名称为 name的已经被加载过的类,返回的结果是 java.lang.Class类的实例。

defineClass(String name, byte[] b, int off, int len)

把字节数组 b中的内容转换成 Java 类,返回的结果是 java.lang.Class类的实例。这个方法被声明为 final的。

resolveClass(Class<?> c)

链接指定的 Java 类。

对于 表 1中给出的方法,表示类名称的 name参数的值是类的二进制名称。需要注意的是内部类的表示,如 com.example.Sample$1com.example.Sample$Inner等表示方式。这些方法会在下面介绍类加载器的工作机制时,做进一步的说明。下面介绍类加载器的树状组织结构。



说明:

Java 中的类加载器大致可以分成两类,一类是系统提供的,另外一类则是由 Java 应用开发人员编写的。系统提供的类加载器主要有下面三个:

1引导类加载器(bootstrap class loader):它用来加载 Java 的核心库JRE/lib/rt.jar),是用原生代码来实现的,并不继承自java.lang.ClassLoader。2扩展类加载器(extensions class loader):它用来加载 Java 的扩展库JRE/lib/ext/*.jarJava虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。

3系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。

除了系统提供的类加载器以外,开发人员可以通过继承 java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。

自定义类加载器:

现在有如下需求:自定义类加载器,并对加载的类加密。
我们来看看如何分步完成的。

1,自定义测试类

ClassLoaderAttachment。该文件处于ClassPath路径下。由前面的知识我们知道,ClassPath路径下的文件由AppClassLoader加载器加载。但

是在实际执行时的顺序我们要明确:通过委托机制,由BootStrap最先开始加载java的核心类库JRE/lib/rt.jar),如果没有找到要加载的类,则由ExtClassLoader类加载器在JRE/lib/ext/*.jar目录下查找要加载的类,如果没有找到,则由AppClassLoaderClassPath路径下查找要加载的类,如果再找不到则抛出ClassNotFoundException

下表的程序清单是定义一个测试类:

import java.util.Date;
public class ClassLoaderAttachment  extends Date{
	public String toString(){
		return "itcast class";
	}
}

下面这段程序是为了测试ClassLoaderAttachment类的类加载器,并通过getParent方法获得父构造器。

public class ClassLoaderTest {
	public static void main(String[] args) {
          ClassLoaderAttachment cla = new ClassLoaderAttachment();
          ClassLoader loader = cla.getClass().getClassLoader();
          while(loader != null){
        	  System.out.println(loader.getClass().getName());
        	  loader = loader.getParent();
          }
          System.out.println(cla);
	}
}

下面是输出结果:

sun.misc.Launcher$AppClassLoader

sun.misc.Launcher$ExtClassLoader

test class


2,自定义类加载器

类 FileSystemClassLoader继承自类 java.lang.ClassLoader。在 表 1中列出的 java.lang.ClassLoader类的常用方法中,一般来说,自己开发的类加载器只需要覆写 findClass(String name)方法即可。java.lang.ClassLoader类的方法loadClass()封装了前面提到的代理模式的实现。该方法会首先调用 findLoadedClass()方法来检查该类是否已经被加载过;如果没有加载过的话,会调用父类加载器的 loadClass()方法来尝试加载该类;如果父类加载器无法加载该类的话,就调用 findClass()方法来查找该类。因此,为了保证类加载器都正确实现代理模式,在开发自己的类加载器时,最好不要覆写 loadClass()方法,而是覆写findClass()方法。

类 FileSystemClassLoader的 findClass()方法首先根据类的全名在硬盘上查找类的字节代码文件(.class 文件),然后读取该文件内容,最后通过 defineClass()方法来把这些字节代码转换成 java.lang.Class类的实例。

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

public class MyClassLoader extends ClassLoader{

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		String srcPath = args[0];//通过主函数参数传递源文件路径
		String destDir = args[1];//通过抓函数参数传递自定义类加载器类文件存放地
		FileInputStream fis = new FileInputStream(srcPath);
		String destFileName = srcPath.substring(srcPath.lastIndexOf('\\')+1);//通过操作字符串得到类文件名
		String destPath = destDir + "\\" + destFileName;//destDir只给出了文件夹,这里给出了完整的路径
		FileOutputStream fos = new FileOutputStream(destPath);
		cypher(fis,fos);
		fis.close();
		fos.close();
	}
	//加密方法
	private static void cypher(InputStream ips ,OutputStream ops) throws Exception{
		int b = -1;
		while((b=ips.read())!=-1){
			ops.write(b ^ 0xff);//对生成的字节码都异或上1111 1111
		}
	}

	private String classDir;
        //要定义自己的类加载器就需要覆盖findClass方法
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		// TODO Auto-generated method stub
		String classFileName = classDir + "\\"  + name.substring(name.lastIndexOf('.')+1) + ".class";//获取文件名称
		try {
			FileInputStream fis = new FileInputStream(classFileName);//输入流对象与要编译的文件想关联
			ByteArrayOutputStream  bos = new ByteArrayOutputStream();//字节输出流对象
			cypher(fis,bos); //调用加密方法
			fis.close();
			System.out.println("aaa");
			byte[] bytes = bos.toByteArray();
			return defineClass(bytes, 0, bytes.length);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	public MyClassLoader(){
		
	}
	
	public MyClassLoader(String classDir){
		this.classDir = classDir;
	}
}
















  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值