类加载器

90 阅读2分钟

类的加载器

作用当然是实现类的加载动作,但是不至于此。

对于确定一个类的唯一性是由他的类加载器和这个类本身决定。每一个类加载器都有一个独立的类名称空间。

双亲委派机智

站在虚拟机角度 只有2种加载器:

第一种是启动类加载器由C语言实现

第二种是其他所有类加载器 由Java实现。

而后来细分为3个系统提供的类加载器加载

1)启动类加载器

2)拓展类加载器

3)应用程序类加载器

双亲委派机制工作原理:

当一个类加载器收到加载类的请求,首先不会自己加载而是逐层将这个请求向上(父类加载器)转发,每一层都如此,因此最终回到启动类加载器进行加载,如果上层无法加载该类,则子类加载器才会自己尝试加载该类。

protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException

{

// 首先,检查请求的类是否已经被加载过了

Class c = findLoadedClass(name);

if (c == null) {

    try {

        if (parent != null) {

            c = parent.loadClass(name, false);

           } else {

            c = findBootstrapClassOrNull(name);

        }

        } catch (ClassNotFoundException e) {

        // 如果父类加载器抛出ClassNotFoundException

            // 说明父类加载器无法完成加载请求

       }
   
   if (c == null) {

        // 在父类加载器无法加载时

        // 再调用本身的findClass方法来进行类加载

        c = findClass(name);

    }

   }

if (resolve) {

    resolveClass(c);

}

上述是双亲委派加载的实现,由此可见,加载器之间是以组合的方式而非继承。

破坏双亲委派机制

1)远古时代 java为了兼容已有代码,无法再以技术的手段避免loadClass()方法被子类覆盖。 后来重写findclass()方法解决了loadClass()问题
2)是由于双亲委派机制的模型自身缺陷导致的,基础类加载器一般总是被用户代码继承,所以为基础,但是并不是一层不变的。如JNDI服务,由启动类加载,但是JNDI存在的目的则是查找和集中管理,使得启动类加载器无法认识,加载那些代码。后来以线程上下文类加载器(Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread类的setContext-ClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器。后来在JDK6后以java.util.ServiceLoader类,以META-INF/services中的配置信息,以责任链模式,这才算是给SPI加载提供了合理的方案
3)热部署,动态加载等