JVM(十二) 创建用户自定义的类加载器

来源:互联网 发布:数据库置疑修复工具 编辑:程序博客网 时间:2024/06/06 00:54

要创建用户自己的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法即可,该方法根据参数指定的类的名字,返回对应的Class对象的引用。

我们来看看官网api文档的findClass方法。

http://docs.oracle.com/javase/7/docs/api/

  • findClass

    protected Class<?> findClass(String name)                      throws ClassNotFoundException
    Finds the class with the specified binary name. This method should be overridden by class loader implementations that follow the delegation model for loading classes, and will be invoked by theloadClass method after checking the parent class loader for the requested class. The default implementation throws aClassNotFoundException.
    Parameters:
    name - The binary name of the class
    Returns:
    The resulting Class object
    Throws:
    ClassNotFoundException - If the class could not be found
    Since:
    1.2
我们来看一个Demo,包括MyClassLoader.java,Sample.java,Dog.java三个文件

MyClass.java

import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.InputStream;public class MyClassLoader extends ClassLoader{private String name;  //类加载器private String path ="d:\\"; //加载类的路径private final String fileType=".class";  //class文件的扩展名private MyClassLoader(String name){super();  //让系统类加载器成为该类加载器的父加载器this.name = name;}public MyClassLoader(ClassLoader parent,String name){super(parent);  //显式指定该类加载器的父加载器this.name = name;}@Overridepublic String toString() {return this.name;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}@Overridepublic Class<?> findClass(String name) throws ClassNotFoundException{byte[] data = this.loadClassData(name);return this.defineClass(name, data, 0,data.length);} private byte[] loadClassData(String name){InputStream is = null;byte[] data = null;ByteArrayOutputStream baos = null;try {this.name= this.name.replace(".", "\\");is = new FileInputStream(new File(path+name+fileType));baos = new ByteArrayOutputStream();int ch = 0;while(-1!=(ch = is.read())){baos.write(ch);}data = baos.toByteArray();} catch (Exception ex) {ex.printStackTrace();}finally{try {is.close();baos.close();} catch (Exception ex) {ex.printStackTrace();}}return data;}public static void main(String[] aregs) throws Exception{MyClassLoader loader1 = new MyClassLoader("loader1");loader1.setPath("d:\\myapp\\serverlib\\");MyClassLoader loader2 = new MyClassLoader(loader1,"loader2");loader2.setPath("d:\\myapp\\clientlib\\");MyClassLoader loader3 = new MyClassLoader(null,"loader3");loader3.setPath("d:\\myapp\\otherlib\\");test(loader2);test(loader3);}public static void test(ClassLoader loader) throws Exception{Class clazz = loader.loadClass("Sample");Object object = clazz.newInstance();}}

Sample.java

public class Sample {public int v1= 1;public Sample(){System.out.println("Sample is loaded by: "+this.getClass().getClassLoader());new Dog();}}

Dog.java

public class Dog {public Dog(){System.out.println("Dog is loaded by :"+this.getClass().getClassLoader());}}

按照以上代码类加载的位置分别创建文件夹。


把编译好的类文件放在各个文件夹下,把Sample.class,Dog.class放在otherlib,serverlib文件夹下。把MyclassLoader.class放在syslib文件夹下。

然后在syslib文件夹下运行dos窗口:

输入命令:java MyClassLoader

输出结果:

Sample is loaded by: loader1
Dog is loaded by :loader1
Sample is loaded by: loader3
Dog is loaded by :loader3

我们分析一下:

findClass方法在上面被重写。


当执行loader2.loadClass("Sample")时,先由它上层的所有父加载器尝试加载Sample类。loader1从D:\myapp\serverlib目录下成功地加载了Sample类,因此loader1是Sample类的定义类加载器,loader1和loader2是Sample类的初始类加载器。

当执行loader3.loadClass("Sample")时,先由它上层的所有父加载器尝试加载Sample类。loader3的父加载器为根类加载器,它无法加载Sample类,接着loader3从D:\myapp\otherlib目录下成功地加载了Sample类,因此loader3是Sample类的定义类加载器及初始类加载器。



在loader1和loader3各自的命名空间中都存在Sample类和Dog类。

在Sample类中主动使用了Dog类,当执行Sample类的构造方法中的new Dog()语句时,Java虚拟机需要先加载Dog类,到底是用哪个类加载器加载呢?从输出结果可以看出,加载Sample类的loader1还加载了Dog类,Java虚拟机会用Sample类的定义类加载器去加载Dog类,加载过程也同样采用父亲委托机制。为了验证这一点,可以把D:\myapp\serverlib目录下的Dog.class文件删除,然后在D:\myapp\syslib目录下存放一个Dog.class文件,此时程序的打印结果为:

Sample is loaded by: loader1
Dog is loaded by :sun.misc.Launcher$AppClassLoader@73d16e93
Sample is loaded by: loader3
Dog is loaded by :loader3

由此可见,当由loader1加载的Sample类首次主动使用Dog类时,Dog类由系统类加载器加载。如果再把D:\myapp\syslib目录下的Dog.class对象去掉,在D:\myapp\clientlib目录下存放一个Dog.class文件


当由loader1加载的Sample类首次主动使用Dog类时,由于loader1及它的父加载器都无法加载Dog类,因此test(loader2)方法会抛出ClassNotFoundException.

拓展:

1、命令中环境变量的指定

输入命令:java -cp .;d:\myapp\serverlib MyClassLoader

输出结果

Sample is loaded by: sun.misc.Launcher$AppClassLoader@73d16e93
Dog is loaded by :sun.misc.Launcher$AppClassLoader@73d16e93
Sample is loaded by: loader3
Dog is loaded by :loader3

分析(仅限test(loader2)):

.;d:\myapp\serverlib指定系统加载器的加载位置。

统加载器从指定的目录D:\myapp\serverlib目录下成功地加载了Sample类,因此系统加载器是Sample类的定义类加载器,系统加载器、loader1和loader2是Sample类的初始类加载器。

2、用代码验证加载器之间的关系

public class Test8 {public static void main(String[] args){ClassLoader classLoader = ClassLoader.getSystemClassLoader();System.out.println(classLoader);while(null!=classLoader){classLoader = classLoader.getParent();System.out.println(classLoader);}}}
输出结果:

sun.misc.Launcher$AppClassLoader@73d16e93
sun.misc.Launcher$ExtClassLoader@15db9742
null

分析:刚开始得到系统加载器,然后得到父加载器应用加载器,然后应用加载器的父加载器的为 null。


0 0
原创粉丝点击