打造自己的类载入器
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类型对象。读者可根据自己载入类的具体方式改写下面代码。
2.由于Java是强类型检查语言,通过网络载入的类被实例化后只是一个Object类型的对象,虚拟机并不知道它包含
哪些方法,应从哪个方法开始执行。因此,可以被动态载入的类必须继承某一个抽象类或实现某一个接口,因为父
类只能有一个,所以通常用实现特定接口的办法。下面的代码定义了一个接口类Share和它的方法start().
3.TestClassLoader类通过使用MyClassLoader类的loadClass()方法,将指定URL地址的类载入并在本地系统执行它
,从而实现了类的动态载入(在执行被载入类的方法前一定要将它进行强制类型转换)
4.Tested类很简单,可以将它将在任何WEB服务器圣桑,但应注意能动态载入且被执行的类,一定要实现预先定义
的接口中的方法,下面的例子实现了接口Share中的start方法。
将Tested放到tomcat根目录下运行:
动态类载入机制的几点应用
1.开发分布式应用。这对开发远程的客户端应用程序最有用,客户端仅需要安装一些基本的系统和一个能实现动态
类载入机制的类;需要本地系统不存在的功能时,仅需要从网络动态载入并执行相应类即可获得特定功能。因为客
户端使用的总是软件的最新版本,所以不再有软件的升级和维护问题,即实现了所谓的“零管理”模式。
2.对.class文件加密。由于Java的字节码容易被反编译,大部分开发java应用程序的公司均担心自己的成果被别人
不劳而获。其实可以将类文件进行适当的加密处理,执行时使用自己的类载入器进行相应的解密,就可以解决这个
问题。
3.使第三方开发者易于扩展应用。从前面可知,所有可以被类载入器动态载入并执行的类,必须继承定义的类或实
现定义的接口,这样,可以制定一些规则,使其他开发者不必了解应用程序也可以扩充功能。
当然,有利必有弊,在网络中使用动态类载入的主要缺陷在于安全性,很可能会载入恶意代码,这个问题要靠java
的安全管理器和适当的加密算法来解决。
程序执行时根据需要由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
的安全管理器和适当的加密算法来解决。
相关文章推荐
- Android视频框架 Vitamio 打造自己的万能播放器
- 使用Vitamio打造自己的Android万能播放器(3)——本地播放(主界面、播放列表)
- 打造自己的多功能USB启动盘——grub2引导WinPE、Archlinux安装镜像和Ubuntu liveCD
- 轻松打造自己的站内搜索引擎
- ViewDragHelper实战 自己打造Drawerlayout
- 打造自己的LINQ Provider(上):Expression Tree揭秘
- 打造自己的dialog
- (五)带属性值的ng-app指令,实现自己定义模块的自己主动载入
- 打造自己的reset.css
- 用extjs 4.0打造自己的WEB桌面之一
- [置顶] 【一步一个脚印】Tomcat+MySQL为自己的APP打造服务器(4)完结篇
- 打造自己的vim界面--用winmanager合并显示NERDTree和TagList
- 打造自己的专业图像工具-Visual C++ 2005图像编程系列【一】
- 【转载】把自己打造成一张"活名片"
- 打造自己的专业图像工具-Visual C++ 2005图像编程系列【二】
- 打造自己的Cacti模板
- swfobject 和 StrobeMediaPlayback 打造自己的flash播放器
- 打造自己的chrome for android
- 使用Vitamio打造自己的Android万能播放器(7)——在线播放(下载视频)
- 使用Vitamio打造自己的Android万能播放器(3)——本地播放(主界面、播放列表)