jvm自定义类加载器

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jvm自定义类加载器相关的知识,希望对你有一定的参考价值。

除了自定义的类加载之外,jvm存在三种类加载器,并以一种父委托的加载机制进行加载。

                           技术分享     

--启动类加载器,又称根加载器,是一个native的方法,使用c++实现。在java中我们用null标识,用于加载jdk自带的类。

--扩展类加载器,用于加载jdk扩展类

--系统类加载器,用于加载classpath目录下的类

 

                技术分享


 

上面提到的三种类加载器,是存在父子关系,即系统类加载器会委托extension加载器,如果extension加载器不能加载该类的话,再由系统类加载器进行加载。注意这里所说的父子关系不是指继承关系,而是一种组合关系。至于为什么要使用这种父委托加载机制呢?

 

说到底还是一种处于安全的考虑,假如存在用户自定义类加载器MyClassLoader,然后用户使用非javac的方式生成字节码文件,这种字节码文件可能存在这样一个类java.lang.string,然后由MyClassLoader 加载这个类,如果不委托到根加载器,那还真让这个伪String去代替了jdk自带的类文件。

 

 

 


 

 

 

实现一个自定义类加载器只需继承ClassLoader,重写findClass()方法

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;

/**
 * @desc 
 * @author chenqm 
 * @date 2016年2月17日
 */
public class MyClassLoader extends ClassLoader {

    private String name; //类加载器的名字
    private String path = "d:\\"; //加载类的路径
    private String fileType = ".class";//Class文件的扩展名
    public MyClassLoader(String name){
        super();
        this.name = name;
    }
    
    public MyClassLoader(ClassLoader parent,String name){
        super(parent);//显式制定该类加载器的父加载器
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }
    
    @Override
    public 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{
            name = name.replace(".", "\\");
            is = new FileInputStream(path + name +fileType);
            baos = new ByteArrayOutputStream();
            int ch =0 ; 
            while(-1 !=(ch = is.read())){
                baos.write(ch);
            }
            data = baos.toByteArray();
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{
                is.close();
                baos.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
       
        return data;
    }
    
    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return name;
    }

    public static void main(String[] args) 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(loader1);
        test(loader2);
        test(loader3);
    }
    
    public static void test(ClassLoader loader) throws Exception{
        Class clazz = loader.loadClass("Simple");
        Object o = clazz.newInstance();
    }
}

 

这里我自定了一个类加载器,用于加载simple类

/**
 * @desc 
 * @author chenqm 
 * @date 2016年2月17日
 */
public class Simple {
    public Simple(){
        System.out.println("Sample is loaded by:"+this.getClass().getClassLoader());
        Dog dog = new Dog();
        System.out.println("Dog is loaded by:"+dog.getClass().getClassLoader());
    }
}

给出dog类:

/**
 * @desc 
 * @author chenqm 
 * @date 2016年2月17日
 */
public class Dog {

}

然后把Dog.class和Simple.class文件放到d:\myapp\serverlib 和d:\myapp\clientlib,将MyClassLoader文件放到d:\myapp\otherlib下面。

 

进入d:\myapp\otherlib 执行java MyClassLoader

得到结果:

D:\myapp\otherlib>java MyClassLoader
Sample:loader1
Dog:loader1
Sample:loader1
Dog:loader1
java.io.FileNotFoundException: d:\myapp\syslib\Simple.class (系统找不到指定的文
件。)

 

 


 

 

 

简单分析一下,请注意MyClassLoader的main方法,有三个自定类加载器loader1(父加载器是系统类加载器),loader2(父加载器是loader1),loader3(父加载器是根加载器)。

test方法:用指定的类加载器去加载simple类。

--用loader1去加载simple类的时候,loader1的父加载器是系统类加载器,系统类加载器肯定无法加载simple类,为什么呢?前面说了,系统类加载器用于加载classpath下的类,而classpath默认是什么?  "."就是当前路径,而当前路径不存在simple类,所以进而由loader1加载。

--loader2的父加载器是loader1,所以根据父委托加载机制,理应由loader1去加载simple类。

--loader3的父加载器是根加载器,根加载器是不会加载我们自定义的类文件的,所以加载的重任就交给了loader3,但是loader3的目录下,不存在simple.class文件,没办法了,父亲干不了,自己也干不了,那只能抛异常咯。


 

以上是关于jvm自定义类加载器的主要内容,如果未能解决你的问题,请参考以下文章

JVM:如何实现一个自定义类加载器?

JVM:如何实现一个自定义类加载器?

JVM自定义类加载器在代码扩展性的实践

JVM 自定义类加载器

java JVM-自定义类加载器

JVM自定义类加载器加载指定classPath下的所有class及jar