ClassLoader源码

ClassLoader源码_第1张图片

jvm中默认定了三种classloader,分别为:bootstrap classloader, extension classloader, system classloader.

bootstrap 使用c语言来实现,没有对应的ClassLoader对象。
该方法String.class.getClassLoader() 返回null。
  
extension 用于从jre/lib/ext 目录加载“标准的扩展”
system 用于加载应用类。由classpath环境变量中的 jar/zip 文件。
  
除此之外,java的classloader采用委托机制,即classloader都有一个 parent classloader。当收到一个类加载请求时,会先请求parent classloader加载类,如果parent classloader家在不到,再由自身尝试加载(如果加载不到,throw ClassNotFountException). 这种机制主要是出于安全考虑,如果用户自定义一个java.lang.Object 不至于覆盖jdk中的Object。

源码分析:

在代码中显式调用加载某个类:

xxx.class.getClassLoader().loadClass("xxx");

classLoader.loadClass(“xxx”)的执行过程:

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

//find class
//findClass()的功能是找到class文件并把字节码加载到内存中。
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 {
                    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.
                    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) {
                resolveClass(c);
            }
            return c;
        }
}

//加载字节码进入内存
//自定义的ClassLoader一般覆盖这个方法,以便使用不同的加载路径。
protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
}

//验证字节码
protected final void resolveClass(Class<?> c) {
        resolveClass0(c);
}

实现类的热部署

JVM默认不能热部署类,因为加载类时会去调用findLoadedClass(),如果类已被加载,就不会再次加载。
JVM判断类是否被加载有两个条件:完整类名是否一样ClassLoader是否是同一个
所以要实现热部署的话,只需要使用ClassLoader的不同实例来加载
ClassLoader源码_第2张图片

自定义ClassLoader

package classloader;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;


public class Main {
    private static class MyClassLoader extends ClassLoader{

        public Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
            Class clazz = null;
            try {
                clazz = findLoadedClass(name); 
                //检查该类是否已经被装载。
                if (clazz != null) {
                    return clazz;   
                }

                byte[] bs = getClassBytes(name);
                //从一个特定的信息源寻找并读取该类的字节。
                if (bs != null && bs.length > 0) {
                    clazz = defineClass(name, bs, 0, bs.length);   
                }
                if (clazz == null) { 
                    //如果读取字节失败,则试图从JDK的系统API中寻找该类。
                    clazz = findSystemClass(name);
                }
                if (resolve && clazz != null) {
                    resolveClass(clazz);   
                }
            } catch (IOException e) {
                throw new ClassNotFoundException(e.toString());
            }   
            System.out.println("class == " + clazz);
            return clazz;
        }   

        private byte[] getClassBytes(String className) throws IOException {
            String path = System.getProperty("java.class.path") + File.separator;
            path += className.replace('.', File.separatorChar) + ".class";
            System.out.println(path);
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(path);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                return null;   
                //如果查找失败,则放弃查找。捕捉这个异常主要是为了过滤JDK的系统API。
            }
            byte[] bs = new byte[fis.available()];
            fis.read(bs);
            return bs;
        }

    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {

        System.out.println(System.getProperty("java.class.path"));

        MyClassLoader cl1 = new MyClassLoader();
        Class c1 = cl1.loadClass("Test");
        c1.newInstance();

        MyClassLoader cl2 = new MyClassLoader();
        Class c2 = cl2.loadClass("Test");
        c2.newInstance();
    }
}

Tomcat的classLoader的作用(实现不同web app 的类隔离)

  1. bootstrap / extension: 加载$JAVA_HOME/jre/lib/ext下的类

  2. system: 加载由CLASSPATH初始化的所有类,对于tomcat自身类以及所有web应用的类可见。

  3. Common: 对于tomcat,和所有web app 可见,用于加载
    $CATALINA_BASE/conf/catalina.properties里面的类,通常应用程序的类不建议放在里面。

  4. WebappX: 加载所有WEB-INF/classes下面的类以及里面的jar。

该classloader有意违背了委托原则。它首先看WEB-INF/classes中是否有请求的类,而不是委托parent classloader去处理,但是jre 和servlet api 不会被覆盖。

你可能感兴趣的:(ClassLoader源码)