关于ClassLoader与动态扩展

来源:互联网 发布:移动网络玩lol不稳定 编辑:程序博客网 时间:2024/06/06 03:27

我们要使用一个类,这个类首先要被装载,然后进行连接,初始化等步骤。一个类的装载是由类装载器(ClassLoader)完成 的。

Java中类装载器大体分为两种,一种是启动类装载器(Bootstrap ClassLoader),这种类装载器是jvm实现的一部分,它不是ClassLoader类的子类; 另一种是用户自定义的类装载器,这类类装载器都是ClassLoader类的子类,Java API中提供了几个自定义类装载器的实现:ExtClassLoader——用来装载标准扩展库中的类,AppClassLoader——一般用来装载我们定义的类。

这里简单的说一下类装载器的双亲委派模型

除了启动类装载器,每一个类装载器都 有一个parent,ExtClassLoader的parent是Bootstrap ClassLoader,AppClassLoader的parent是ExtClassLoader,像我们自定义的ClassLoader,如果不明确指定parent,其parent都 默认是AppClassLoader。可以通过调用ClassLoader实例的getParent()方法来获取其parent,如果其双亲 是Bootstrap ClassLoader,这个方法将返回null。当一个类装载器被请求装载一个类型时,它首先请求其parent来装载这个类型,其parent再向上请求直到Bootstrap ClassLoader,如果其parent能够装载这个类开则返回代表这个类型的Class实例,如果不能此 ClassLoader尝试装载,如果成功返回代表这个类型的Class实例,否则抛出异常。

在Java术语中,要求某个类装载器去装载一个类型,但是却返回了其他类装载器装载的类型,这种装载器被称为是那个类型的初始类装载器,而实际定义那个类型的类装载器称为该类型的定义类装载器

下面我们通过一个简的例子来看一下 我们平时定义的类和API中的类都 是通过哪些ClassLoader来装载的。

 

ClassLoaderDemo.java

package kevin;import java.util.List;public class ClassLoaderDemo {public static void main(String[] args) {Class thisCls = ClassLoaderDemo.class;System.out.println("the class loader of this class with main method is:"+ thisCls.getClassLoader());Class sysCls = System.class;System.out.println("the loader of the System class is: "+ sysCls.getClassLoader());Class listCls = List.class;System.out.println("the loader of the List class in package java.util is:"+ listCls.getClassLoader());/* * 以上三句的打印结果如下:  * the class loader of this class with main methodis:sun.misc.Launcher$AppClassLoader@19821f  * the loader of the System class is: null  * the loader of the List class in package java.util is:null  * 由此可见我们自定义的类是默认是由AppClassLoader装载的,而API中的类是由BootStrap * ClassLoader,即启动类装载 器装载的 */}}

Java允许动态扩展程序,这个过程包括运行时决定所使用的类型,装载它们,使用它们。动态扩展一般可以通过两种方法来实现,第一种是使用自定义的类装载器的loadClass()方法,另一种就是使用Class.forName()方法。

      下面我们首先通过自定义类装载器的方法来动态扩展java程序。

Hello.java

package kevin;public class Hello {public void sayHello(){System.out.println("Hello,free world...");}}

HelloClassLoader.java

package kevin;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;/** * 自定义一个类装载器 */public class HelloClassLoader extends ClassLoader {/** * 类所在的目录路径 */private String basePath;public HelloClassLoader(String basePath) {this.basePath = basePath;}public HelloClassLoader(ClassLoader parent, String basePath) {super(parent);this.basePath = basePath;}/** * 一般我们要重写这个方法,然后将代表这个class文件的byte数组数据传递给defineClass方法,它会将这个数组 * 转换在一个代表此类的Class实例返回 */@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {// TODO Auto-generated method stubbyte[] data = getClassData(name);if (data == null) {throw new ClassNotFoundException();}return defineClass(name, data, 0, data.length);}/** * 获取类文件的字节数据 * @param name 类的全限定名 * @return 包含类数据的byte型数组 */public byte[] getClassData(String name) {String filePath = basePath + name.replace(".", File.separator);byte[] buf = null;try {FileInputStream in = new FileInputStream(filePath);buf = new byte[in.available()];in.read(buf);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}return buf;}}

CustomLoaderDemo.java


package kevin;public class CustomLoaderDemo {public static void main(String[] args) {//创建自定义的ClassLoader的实例HelloClassLoader loader = new HelloClassLoader("");System.out.println(loader.getParent());try {//用自定义的类装载器来装载类,这是动态扩展的一种途径Class cls = loader.loadClass("kevin.Hello");System.out.println(cls.getClassLoader());Hello hello = (Hello) cls.newInstance();hello.sayHello();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}}



虽然我们请求让自定义的类装载器HelloClassLoader来装载Hello.class,但是该类 的定义类装载器却是AppClassLoader,这个可以通过上面说过的双亲委派模型来解释。

    下面是一个forName()的例子。使用forName()可以保证在这个方法返回之前 ,被装载的类型会被连接和初始化,而loadClass()不能对此做出承诺。


EasyHello.java

package kevin;public class EasyHello {public static void main(String[] args) {// 动态扩展的另一种途径是用Class类的静态方法forName()try {Class cls = Class.forName("kevin.Hello");System.out.println(cls.getClassLoader());Hello hello = (Hello) cls.newInstance();hello.sayHello();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}}



上面是些简单的例子,希望对大家有点帮助。。如果大牛路过看到什么错误 了,还望不吝赐教。。。