您的位置:首页 > 其它

打造自己的类载入器

2012-03-14 15:33 183 查看
Java是一种极具动态性的语言。类似Windows的动态链接库,Java应用程序总是被编译成若干个单独的class文件,

程序执行时根据需要由java虚拟机动态载入相关的类。这种机制使编写动态的分布式应用程序成为可能:我们可以

在客户端编写自己的类载入器,而真正执行的程序却存放在本地、局域网或世界另一端的主机上。下面将介绍

如何在应用程序中实现java的动态类载入机制。

与动态类载入有关的系统类为支持动态类载入机制,系统类组java.lang中提供了两个类:Class类和ClassLoader

类。

Class类

在java虚拟机中,每一个类或接口都是由Class类来操纵的,它不能被显式的的实例化。必须使用其他方

法来获取Class类的对象。动态类载入机制的关键在于如何获得指定类的Class对象。相关方法主要有:

public static Class forName(String className)

这是一个静态方法,它获取指定名字的类的Class类型对象,类名可以是像"sun.applet.Applet"这样的字符串,但

不能带有路径或网络地址等信息。这是从本地系统中动态载入类的最方便的办法。

public Object newInstance()

这是最重要的一个方法,它建立由Class类型对象描述的指定类的实例。

ClassLoader类

这是一个抽象类,如果打算运用它,必须继承它并重写它的loadClass()方法。其主要方法有:

protected ClassLoader()

这是一个构建元,可以用它建议一个ClassLoader类的实例。注意:继承这个类的类必须重写这个方法,而不能使

用缺省的构建元。

protected abstract Class loadClass(String name, boolean resolve)

载入指定的类数据,建立Class类型的对象并根据需要解析它。这是一个抽象方法。大家必须在自己的子类中

重写这个方法,重写的规则可以参考第三部分的例子。

protected final Class defineClass(byte data[], int offset, int length)

将字节数组中的数据定义为Class类型的对象,字节数组的格式由虚拟机规定。

protected final Class findSystemClass(String name)

根据指定的类名载入类,它会自动在当前目录和环境变量classpath指定的路径中寻找,如果找不到,则会抛出

ClassNotFouondException异常。

protected final void resolveClass(Class c)

通过载入与指定的类相关的所有类来解析这个类,这必须在类使用之前完成。

扩充ClassLoader类以实现动态类载入

理解动态类载入机制最好的办法是通过例子,下面这个完整的例子由四个类组成,分别解释如下。

1.MyClassLoader类是ClassLoader类的子类,它重写了loadClass方法,实现了将网络上用URL地址指定的类动态载

入,取得它的Class类型对象。读者可根据自己载入类的具体方式改写下面代码。

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Hashtable;

@SuppressWarnings("all")
public class MyClassLoader extends ClassLoader {
private Hashtable loadedClasses;

// 必须定义
public MyClassLoader() {
loadedClasses = new Hashtable();
}

public synchronized Class<?> loadClass(String className, boolean resolve) throws

ClassNotFoundException {
Class newClass;
byte[] classData; // 存放类的字节数组
// 检查要载入的类数据是否已经被保存在哈希表中
newClass = (Class) loadedClasses.get(className);
// 如果类数据已经存在且resolve值为true,则解析它
if (newClass != null) {
if (resolve) resolveClass(newClass);// 链接类
return newClass;
}
/*
* 首先必须从本地类组中载入指定类,因为虚拟机将这个类载入后,在
* 解析和执行它时所用到的任何其他类等,均不再使用虚拟机的类载入器
* 而是调用我们自制的类载入器来加载
*/
try {
newClass = findSystemClass(className);
return newClass;
} catch (ClassNotFoundException e) {
System.out.println(className + " is not a system class");
}
// 如果不是系统类,则试图从网络中指定的URL地址载入类
try {
// 用自定义方法载入数据,存放于字节数组classData中
classData = getClassData(className);
// 用字节数组包含的数据及案例一个class类的对象
newClass = defineClass(classData, 0, classData.length);
if (newClass == null) throw new ClassNotFoundException(className);
} catch (Exception e) {
throw new ClassNotFoundException(className);
}
// 如果类正确载入,则将类保存在哈希表中
loadedClasses.put(className, newClass);
if (resolve) {
resolveClass(newClass);
}
return newClass;
}

protected byte[] getClassData(String className) throws IOException {
byte[] data;
int length;
try {
// 从网络中采用URL类的方式载入指定URL地址的类数据
URL url = new URL(className.endsWith(".class") ? className : className +

".class");
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();
length = conn.getContentLength();// 返回 content-length 头字段的值
data = new byte[length];
is.read(data);
is.close();
return data;
} catch (Exception e) {
throw new IOException(className);
}
}
}


2.由于Java是强类型检查语言,通过网络载入的类被实例化后只是一个Object类型的对象,虚拟机并不知道它包含

哪些方法,应从哪个方法开始执行。因此,可以被动态载入的类必须继承某一个抽象类或实现某一个接口,因为父

类只能有一个,所以通常用实现特定接口的办法。下面的代码定义了一个接口类Share和它的方法start().

public interface Share {
public void start(String[] args);
}


3.TestClassLoader类通过使用MyClassLoader类的loadClass()方法,将指定URL地址的类载入并在本地系统执行它

,从而实现了类的动态载入(在执行被载入类的方法前一定要将它进行强制类型转换)

public class TestClassLoader {
@SuppressWarnings("all")
public static void main(String[] args) {
MyClassLoader loader = new MyClassLoader();
Class cc;
Object oo;
String ss = "http://localhost:8888/Tested.class";
if (args.length != 0) ss = args[0];
try {
System.out.println("Loading class " + ss + " ...");
// 使用重写的方法loadClass载入类数据
cc = loader.loadClass(ss);
System.out.println("Create instance...");
oo = cc.newInstance();
System.out.println("Call start method");
// 强制类型转换后执行载入类中的方法
((Share) oo).start(args);
} catch (Exception e) {
System.out.println("Caught exception: " + e);
}
}
}


4.Tested类很简单,可以将它将在任何WEB服务器圣桑,但应注意能动态载入且被执行的类,一定要实现预先定义

的接口中的方法,下面的例子实现了接口Share中的start方法。

public class Tested implements Share {
@Override
public void start(String[] args) {
System.out.println("动态载入类成功");
for (String arg : args) {
System.out.println(arg);
}
}
}

将Tested放到tomcat根目录下运行:



动态类载入机制的几点应用

1.开发分布式应用。这对开发远程的客户端应用程序最有用,客户端仅需要安装一些基本的系统和一个能实现动态

类载入机制的类;需要本地系统不存在的功能时,仅需要从网络动态载入并执行相应类即可获得特定功能。因为客

户端使用的总是软件的最新版本,所以不再有软件的升级和维护问题,即实现了所谓的“零管理”模式。

2.对.class文件加密。由于Java的字节码容易被反编译,大部分开发java应用程序的公司均担心自己的成果被别人

不劳而获。其实可以将类文件进行适当的加密处理,执行时使用自己的类载入器进行相应的解密,就可以解决这个

问题。

3.使第三方开发者易于扩展应用。从前面可知,所有可以被类载入器动态载入并执行的类,必须继承定义的类或实

现定义的接口,这样,可以制定一些规则,使其他开发者不必了解应用程序也可以扩充功能。

当然,有利必有弊,在网络中使用动态类载入的主要缺陷在于安全性,很可能会载入恶意代码,这个问题要靠java

的安全管理器和适当的加密算法来解决。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: