初探JVM-ClassLoader源码(二)

2014-11-24 08:20:04 · 作者: · 浏览: 1
classpath来说,他可以指向任意可达的URL来加载类资源。

从类的继承结构来看,URLClassLoader->SecureClassLoader->ClassLoader,URLClassLoader间接继承了ClassLoader。

public class URLClassLoader extends SecureClassLoaderimplements Closeable

在URLClassLoader自定义的类加载策略中,没有修改loadClass(),而只是重写了findClass()。他会把我们传入的类名"com.jscai.classloader.Ext4Test",根据构造函数中的path,拼装成.class文件名"com/jscai/classloader/Ext4Test.class",然后在defineClass()中把.class文件加载成类对象。

在执行URLClassLoader.loadClass()的时候,因为是URL指向的类资源在classpath以外,所以在委派模型上面是找不到的。这时候抛出ClassNotFound异常,跳到URLClassLoader.findClass()来执行自定义的加载。

    protected Class
   findClass(final String name)throws ClassNotFoundException{
        try {
            return (Class) AccessController.doPrivileged(newPrivilegedExceptionAction() {
                public Object run()throws ClassNotFoundException {
                    String path = name.replace('.','/').concat(".class");
                    Resource res = ucp.getResource(path,false);
                    if (res !=null) {
                        try {
                            return defineClass(name,res,true);
                        }
                        catch (IOException e) {
                            thrownewClassNotFoundException(name, e);
                        }
                    }
                    else {
                        thrownewClassNotFoundException(name);
                    }
                }
            }, acc);
        }
        catch (java.security.PrivilegedActionExceptionpae) {
            throw (ClassNotFoundException) pae.getException();
        }
    }

这里写个简单的URLClassLoader-demo,先创建一个简单的测试类Ext4Test,然后写一个测试方法来证明JVM能正常加载:

        System.out.println(Class.forName("com.jscai.classloader.Ext4Test"));

然后到bin目录把Ext4Test.class连同package文件夹(因为URLClassLoader会根据包名解析成目录结构)放到根目录下的newClass目录,然后把Ext4Test类删掉。再运行刚刚的测试方法,会抛出ClassNotFoundException。说明此时Ext4Test类不在classpath,JVM的类加载器已经找不到它了。

这时候再指定URLClassLoader来加载,这时候能正常运行,说明URLClassLoader能找到classpath以外的Ext4Test.class。

        String url = "file:/" + System.getProperty("user.dir") +"/newClass/";
        URLClassLoader ucl = new URLClassLoader(new URL[]{new URL(url)});
        Class
   c = ucl.loadClass("com.jscai.classloader.Ext4Test");
        ClassLoader loader = c.getClassLoader();
        while (loader !=null) {
            System.out.println(loader.toString());
            loader = loader.getParent();
        }

console:

java.net.URLClassLoader@248523a0

sun.misc.Launcher$AppClassLoader@79b0edb2

sun.misc.Launcher$ExtClassLoader@4ec57f88

而且这里能看出,类加载遵循委派模型,URLClassLoader位于JVM的类加载器之后。

所以,这里如果只是加载classpath以内的class,那JVM类加载器加载完就结束了,即使用URLClassLoader来加载,也不会经过URLClassLoader的。

        String InnerName = "com.jscai.classloader.Inner4Test";
        URLClassLoader ucl = new URLClassLoader(new URL[]{new URL(url)});
        Class
   c = ucl.loadClass(InnerName);
        ClassLoader loader = c.getClassLoader();
        while (loader != null) {
            System.out.println(loader.toString());
            loader = loader.getParent();
        }

console:

sun.misc.Launcher$AppClassLoader@79b0edb2

sun.misc.Launcher$ExtClassLoader@4ec57f88

动态加载--Class的识别

了解了JVM的ClassLoader和自定义类加载器后,我们谈谈ClassLoader比较火的应用--类的动态加载/装载。

类的动态加载就是在不重启JVM的前提下,把新的class加载到内存中(新的class对象覆盖旧的class对象)。那为什么ClassLoader能动态加载,JVM中是怎么管理class对象的, JVM中是怎么辨别class对象的新旧的,……等等很多问题都是值得我们去思考和探究,在这里我们先讨论,JVM中是怎么识别一个class对象的。

在JVM中,唯一标识一个class对象需要看两个条件:A类全名(包名+类名),B加载器实例。

就是说,只要是同一个类加载器实例加载的同一个名字的class对象,JVM都认为是同一个类。

        Class
   c1 = Class.forName("com.jscai.classloader.Inner4Test");
        Class
   c2 = Class.forName("com.jscai.classloader.Inner4Test");
        System.out.println("They aresam