首页 > 代码库 > ClassLoader类加载器

ClassLoader类加载器

先看例子:

public class ClassLoaderTest{
    public static void main(String[] args) {
        ClassLoader cl = ClassLoaderTest.class.getClassLoader();
        System.out.println(cl);
        System.out.println(cl.getParent());
        System.out.println(cl.getParent().getParent());
        /**
         * List是rt.jar包下
         */
        System.out.println(List.class.getClassLoader());
    }
}

输出结果:

sun.misc.Launcher$AppClassLoader@6fd7bd04
sun.misc.Launcher$ExtClassLoader@3cba8af9
null
null


系统默认三个类加载器

BootStrap(根加载器):java核心库(rt.jar),C/C++写,故而打印出 null

ExtClassLoader(扩展加载器):ext包下

AppClassLoader:classpath下


类加载器的委托机制:

当Java虚拟机要加载第一个类的时候,到底派出哪个类加载器去加载呢?

(1). 首先当前线程的类加载器去加载线程中的第一个类(当前线程的类加载器:Thread类中有一个get/setContextClassLoader(ClassLoader cl);方法,可以获取/指定本线程中的类加载器)

(2). 如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B

(3). 还可以直接调用ClassLoader.loadClass(String className)方法来指定某个类加载器去加载某个类

每个类加载器加载类时,又先委托给其上级类加载器当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则会抛出ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild()方法。例如:如上图所示: MyClassLoader->AppClassLoader->ExtClassLoader->BootStrap.自定定义的MyClassLoader1首先会先委托给AppClassLoader,AppClassLoader会委托给ExtClassLoader,ExtClassLoader会委托给BootStrap,这时候BootStrap就去加载,如果加载成功,就结束了。如果加载失败,就交给ExtClassLoader去加载,如果ExtClassLoader加载成功了,就结束了,如果加载失败就交给AppClassLoader加载,如果加载成功,就结束了,如果加载失败,就交给自定义的MyClassLoader1类加载器加载,如果加载失败,就报ClassNotFoundException异常,结束。


在看一个例子 :

public class MyClassLoader extends ClassLoader{
    protected MyClassLoader(ClassLoader parent) {
        super(parent);
    }
    protected MyClassLoader() {
        super();
    }
}

public class ClassLoaderTest2 {
    public static void main(String[] args) {
        MyClassLoader ml1 = new MyClassLoader();
        System.out.println(ml1.getParent());
        MyClassLoader ml2 = new MyClassLoader(Thread.currentThread().getContextClassLoader().getParent());
        System.out.println(ml2.getParent());
    }
}

打印结果:

sun.misc.Launcher$AppClassLoader@2ac510e3
sun.misc.Launcher$ExtClassLoader@6fd7bd04

说明自定义类加载器的父加载器默认是 :AppClassLoader


自定义的类加载器必须继承抽象类ClassLoader

一般只需 重写findClass方法,其实他内部还有一个loadClass方法和defineClass方法;

看loadClass源码:

public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    //同步处理
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        //检查该类是否已经加载
        Class c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                //如果有parent,就让parent加载,如果找不到该类,
                //抛ClassNotFoundException ,让子加载器加载,直到加载成功结束
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    //如果自定义加载器parent为null,直接用根加载器加载
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                //自定义加载器重写该方法
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            解析class文件
            resolveClass(c);
        }
        return c;
    }
}

//该方法直接抛出异常,就是为了重写,所以自定义加载器只需要重写findClass,如果重写
// load方法,还需要重新写容器的委托逻辑, 没有必要
protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

defineClass这个方法很简单就是将class文件的字节数组编程一个class对象,这个方法肯定不能重写,内部实现是在C/C++代码中实现的


自定义类加载器:

public class MyClassLoader extends ClassLoader{
    private String base_dir;

    protected MyClassLoader(ClassLoader parent) {
        super(parent);
    }
    protected MyClassLoader() {
        super();
    }
    public MyClassLoader(ClassLoader parent,String base_dir) {
            super(parent);
            this.base_dir = base_dir;
        }

    public MyClassLoader(String base_dir) {
        super();
        this.base_dir = base_dir;
    }

    //重写findClass
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String path = this.base_dir+"\\"+name+".class";
        byte[] b = new byte[0];
        try {
            b = toByteArray(path);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(b.length == 0){
            return null;
        }
        //定义class信息,把class文件字节码信息组装成jvm的class信息
        return defineClass(null,b,0,b.length);
    }

    private byte[] toByteArray(String filename) throws IOException {

        File f = new File(filename);
        if (!f.exists()) {
            throw new FileNotFoundException(filename);
        }

        ByteArrayOutputStream bos = new ByteArrayOutputStream((int) f.length());
        BufferedInputStream in = null;
        try {
            in = new BufferedInputStream(new FileInputStream(f));
            int buf_size = 1024;
            byte[] buffer = new byte[buf_size];
            int len = 0;
            while (-1 != (len = in.read(buffer, 0, buf_size))) {
                bos.write(buffer, 0, len);
            }
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            bos.close();
        }
    }

}

public class ClassLoaderTest3 {
    public static void main(String[] args) {
        MyClassLoader ml = new MyClassLoader("E:\\workspace\\mytest\\temp");
        try {
            Class helloClass = ml.loadClass("Hello");
            Object hello =  helloClass.newInstance();
            System.out.println(hello.getClass().getClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
}

打印结果:

classloader.MyClassLoader@19ad0dd8

这里要声明:很容易打印出 AppClassLoader,这是由于你本地idea创建的Hello.class文件可能存在 项目class目录下,删除,从其他目录引入 即可;

现在来测试下自定义的类加载器:

public class Hello {
    public String sayHello(){
        return "hello classLoader";
    }
}

public class ClassLoaderTest3 {
    public static void main(String[] args) {
        //指定根路径,可以根据实际情况引入
        MyClassLoader ml = new MyClassLoader(ClassLoader.getSystemClassLoader().getParent(),"E:\\workspace\\mytest\\temp");
        try {
            //加载
            Class helloClass = ml.loadClass("Hello");
            //初始化
            Object hello =  helloClass.newInstance();
            //调用  加载类的方法
            Object info = helloClass.getMethod("sayHello").invoke(hello);
            System.out.println(info);
            System.out.println(hello.getClass().getClassLoader());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

打印结果:

hello classLoader
classloader.MyClassLoader@19ad0dd8






本文出自 “11898338” 博客,谢绝转载!

ClassLoader类加载器