类加载器,类加载
类加载器,类加载
类加载器基本概念
顾名思义,类加载器(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$1和com.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/*.jar)。Java虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 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目录下查找要加载的类,如果没有找到,则由AppClassLoader在ClassPath路径下查找要加载的类,如果再找不到则抛出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;
}
}
相关文章
- 暂无相关文章
用户点评