Java的ClassLoader机制解析

JVM在加载类的时候,都是通过ClassLoader的loadClass()方法来加载class的,loadClass(String name)方法:
使用的是双亲委托模式:
jvm启动时,会启动jre/rt.jar里的类加载器:bootstrap classloader,用来加载java核心api;然后启动扩展类加载器ExtClassLoader加载扩展类,并加载用户程序加载器 AppClassLoader,并指定ExtClassLoader为他的父类;
当类被加载时,会先检查在内存中是否已经被加载,如果是,则不再加载,如果没有,再由AppClassLoader来加载,先从jar包里找,没有再从classpath里找;
如果自定义loader类,就会存在这命名空间的情况,不同的加载器加载同一个类时,产生的实例其实是不同的;

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

loadClass(String name)方法再调用loadClass(String name, boolean resolve)方法:
     - name - 类的二进制名称
     - resolve - 如果该参数为 true,则分析这个类
Java代码
protected synchronized Class<?> loadClass(String name, boolean resolve)  
    throws ClassNotFoundException  
    {  
    // First, check if the class has already been loaded  
    //JVM 规范规定ClassLoader可以在缓存保留它所加载的Class,如果一个Class已经被加载过,则直接从缓存中获取  
    Class c = findLoadedClass(name);  
    if (c == null) {  
        try {  
        if (parent != null) {  
            c = parent.loadClass(name, false);  
        } else {  
            c = findBootstrapClass0(name);  
        }  
        } catch (ClassNotFoundException e) {  
            // If still not found, then invoke findClass in order  
            // to find the class.  
            c = findClass(name);  
        }  
    }  
    if (resolve) {  
        resolveClass(c);  
    }  
    return c;  
}

如果ClassLoader并没有加载这个class,则调用findBootstrapClass0:
Java代码
private Class findBootstrapClass0(String name)  
    throws ClassNotFoundException  
    {  
    check();  
    if (!checkName(name))  
        throw new ClassNotFoundException(name);  
    return findBootstrapClass(name);  
    }
该方法会调用check()方法来判断这个类是否已经初始化,并且通过checkName(name)来判断由name指定的这个类是否存在
最后调用findBootstrapClass(name):
Java代码
private native Class findBootstrapClass(String name)  
    throws ClassNotFoundException; 

而这个findBootstrapClass方法是一个native方法,这是我们的root loader,这个载入方法并非是由JAVA所写,而是C++写的,它会最终调用JVM中的原生findBootstrapClass方法来完成类的加载。

如果上面两个都找不到,则使用findClass(name)来查找指定类名的Class:
Java代码
protected Class<?> findClass(String name) throws ClassNotFoundException {  
    throw new ClassNotFoundException(name);  
}

JDK5.0中的说明:
使用指定的二进制名称查找类。此方法应该被类加载器的实现重写,该实现按照委托模型来加载类。在通过父类加载器检查所请求的类后,此方法将被 loadClass 方法调用。默认实现抛出一个 ClassNotFoundException。
所以,我们在自定义类中,只需要重写findClass()即可。
MyClassLoader类:
Java代码
public class MyClassLoader extends ClassLoader {  
    private String fileName;  
 
    public MyClassLoader(String fileName) {  
        this.fileName = fileName;  
    }  
 
    protected Class<?> findClass(String className) throws ClassNotFoundException {  
        Class clazz = this.findLoadedClass(className);  
        if (null == clazz) {  
            try {  
                String classFile = getClassFile(className);  
                FileInputStream fis = new FileInputStream(classFile);  
                FileChannel fileC = fis.getChannel();  
                ByteArrayOutputStream baos = new 
                        ByteArrayOutputStream();  
                WritableByteChannel outC = Channels.newChannel(baos);  
                ByteBuffer buffer = ByteBuffer.allocateDirect(1024);  
                while (true) {  
                    int i = fileC.read(buffer);  
                    if (i == 0 || i == -1) {  
                        break;  
                    }  
                    buffer.flip();  
                    outC.write(buffer);  
                    buffer.clear();  
                }  
                fis.close();  
                byte[] bytes = baos.toByteArray();  
 
                clazz = defineClass(className, bytes, 0, bytes.length);  
            } catch (FileNotFoundException e) {  
                e.printStackTrace();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
        return clazz;  
    }  
    private byte[] loadClassBytes(String className) throws 
            ClassNotFoundException {  
        try {  
            String classFile = getClassFile(className);  
            FileInputStream fis = new FileInputStream(classFile);  
            FileChannel fileC = fis.getChannel();  
            ByteArrayOutputStream baos = new 
                    ByteArrayOutputStream();  
            WritableByteChannel outC = Channels.newChannel(baos);  
            ByteBuffer buffer = ByteBuffer.allocateDirect(1024);  
            while (true) {  
                int i = fileC.read(buffer);  
                if (i == 0 || i == -1) {  
                    break;  
                }  
                buffer.flip();  
                outC.write(buffer);  
                buffer.clear();  
            }  
            fis.close();  
            return baos.toByteArray();  
        } catch (IOException fnfe) {  
            throw new ClassNotFoundException(className);  
        }  
    }  
    private String getClassFile(String name) {  
        StringBuffer sb = new StringBuffer(fileName);  
        name = name.replace('.', File.separatorChar) + ".class";  
        sb.append(File.separator + name);  
        return sb.toString();  
    }  
}

该类中通过调用defineClass(String name, byte[] b, int off, int len)方法来定义一个类:
Java代码
protected final Class<?> defineClass(String name, byte[] b, int off, int len)  
throws ClassFormatError  
   {  
return defineClass(name, b, off, len, null);  
   }

注:MyClassLoader加载类时有一个局限,必需指定.class文件,而不能指定.jar文件。该类中的大部分代码是从网上搜索到的,是出自一牛人之笔,只是不知道原帖在哪,希望不会被隐藏。
MainClassLoader类:
Java代码
public class MainClassLoader {  
    public static void main(String[] args) {  
        try {  
            MyClassLoader tc = new MyClassLoader("F:\\OpenLib\\");  
            Class c = tc.findClass("Test");  
            c.newInstance();  
        } catch (ClassNotFoundException e) {  
            e.printStackTrace();   
        } catch (IllegalAccessException e) {  
            e.printStackTrace();  
        } catch (InstantiationException e) {  
            e.printStackTrace();   
        }  
    }  
}

最后是一个简单的Test测试类:
Java代码
public class Test  
{  
    public Test() {  
        System.out.println("Test");  
    }  
    public static void main(String[] args) {  
        System.out.println("Hello World");  
    }  

你可能感兴趣的:(ClassLoader)