java自带的classloader

sun jre本身的classloader主要包含三个loader,分别是
1. Bootstrap class loader->C代码  (加载%java_home%/lib/*)
2. Extensions class loader->ExtClassLoader    (%java_home%/lib/ext/*)
3. System class loader->AppClassLoader        (加载classpath下的类)

 

为什么他们会加载这些路径下面的class呢?
ExtClassLoader和AppClassLoader的构造函数可以看出

 

        String s = System.getProperty("java.class.path");
        new ExtClassLoader(s, extcl);
       
        String s = System.getProperty("java.ext.dirs");
        new AppClassLoader(s, extcl);

 


ExtClassLoader和AppClassLoader都继承于URLClassLoader,构造函数传递的path将来会作为加载class的路径范围

(注: ExtClassLoader和AppClassLoader不属于标准的java api,是sun.misc.Launcher的内部类,具体源码在openjdk中,在线地址为
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/misc/Launcher.java


那如何加载字节码class文件呢?
以AppClassLoader为例,当需要它加载A.class时,会调用其loadClass(String name, boolean resolve)方法,
此方法会通过super.loadClass(name, resolve)交由父类,最终由java.lang.ClassLoader处理,代码如下
(java.lang.ClassLoader.loadClass方法)

    protected synchronized Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
    {
    // First, check if the class has already been loaded
    Class c = findLoadedClass(name);
    if (c == null) {
        try {
        if (parent != null) {
            c = parent.loadClass(name, false);
        } else {
            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.
            c = findClass(name);
        }
    }
    if (resolve) {
        resolveClass(c);
    }
    return c;
    }
 


从这段代码可以看出加载class有一个委托的模型,AppClassLoader,ExtClassLoader, Bootstrap classoader在建立初期就创建了一个所谓的父子关系(并不是继承关系),
如图
   java自带的classloader
其后在加载具体class的时候会有个委托parent classloader加载,parent加载不到,再由自己加载的过程,目的就是为了安全,防止java的核心类库被修改,如果用这种模型,java.lang.String类就不能由用户自己去实现,
因为他肯定最终还是Bootstrap classoader类去加载了,因为string.class是在%java_home%/lib/*下的rt.jar中
(注:如果你一定要自己写一个java.lang.String覆盖jre自己的,你可以自己实现classloader,然后违反委托模型,这是可以做到的)


可以看到上面的java.lang.ClassLoader.loadClass方法会调用 findClass(name)去真正得到一个class对象,此方法有ClassLoader的子类,ExtClassLoader和AppClassLoader的父类URLClassLoader实现,
具体方法实现如下(去掉不影响理解的代码)
(java.lang.URLClassLoader.findClass方法)

    protected Class<?> findClass(final String name) {
            String path = name.replace('.', '/').concat(".class");
            //ucp变量就是当初构造AppClassLoader的时候传递的路径,这里使用到
            Resource res = ucp.getResource(path, false);
            return defineClass(name, res, true);
    }
 


   
上面会继续调用到defineClass方法
(java.lang.URLClassLoader.defineClass方法)

private Class defineClass(String name, Resource res, boolean verify)
        throws IOException {
    int i = name.lastIndexOf('.');
    URL url = res.getCodeSourceURL();
 
    // Now read the class bytes and define the class
    ByteBuffer bb = res.getByteBuffer();
        byte[] bytes = ((bb == null)? res.getBytes() : null);
        // NOTE: Must read certificates AFTER reading bytes above.
        CodeSigner[] signers = res.getCodeSigners();
        CodeSource cs = new CodeSource(url, signers);

        if (!verify) {
            // Need to use reflection since methods are private in super class
            Object[] args = {
                name, (bb == null? ByteBuffer.wrap(bytes) : bb), cs
            };
            try {
                return (Class) defineClassNoVerifyMethod.invoke(this, args);
            } catch (IllegalAccessException iae) {
                // Should never happen; fall back to the regular defineClass
            } 
        }
        return (bb != null? defineClass(name, bb, cs) :
                        defineClass(name, bytes, 0, bytes.length, cs));
    }
 

上面方法会先通过stream的方式读取字节码转为byte数组,然后最终交由ClassLoader的defineClass1方法
(java.lang.ClassLoader.defineClass1方法)

  private native Class defineClass1(String name, byte[] b, int off, int len,
                                  ProtectionDomain pd, String source,
                                      boolean verify);
 


上面方法是native方法,这是加载字节码的最后一步,可以想象,这必须由jvm本身来实现

以上总结:加载class首先有个委托的过程,而加载本身又是经历根据路径寻找字节码,加载字节流,讲字节流转为Class对象的过程

你可能感兴趣的:(java,jvm)