7.类加载器及双亲委派模型

来源:互联网 发布:java apache 框架 编辑:程序博客网 时间:2024/05/17 08:23

1.类加载器与类的唯一性

类加载器用于实现类的加载动作,但是对于任意一个类都需要由加载他的类加载器和类本身共同确立其在Java虚拟机中的唯一性。每一个类加载器都有一个独立的命名空间。比较两个类相等,只有在两个类是有同一个类加载器加载的前提下才有可能相等,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要类加载器不同,那么这两个类必定是不相等的。

2.类加载器的种类及其关系

2.1 Bootstrap ClassLoader(引导类加载器)

作为JVM的一部分,无法再应用程序中引用。由c++实现。负责加载核心类库JR/lib/(他只会加载特定的类库),-Xbootclasspath参数所指定的目录或系统属性sun.boot.class.path指定的目录。在JVM启动时将通过Bootstrap ClassLoader加载rt.jar,并初始化sun.misc.Launcher从而创建Extension ClassLoader和System ClassLoader实例,和将System ClassLoader实例设置为主线程的默认Context ClassLoader(线程上下文加载器)。

2.1.@ sun.misc.Launcher

Java程序的入口是该类,Launcher类初始化时,先初始化了个ExtClassLoader,然后又初始化了个AppClassLoader,然后把ExtClassLoader作为AppClassLoader的父loader。

ExtClassLoader没有指定父类,即表明,父类是BootstrapClassLoader。
把初始化的AppClassLoader 作为全局变量保存起来,并设置到当前线程contextClassLoader。
每个线程实例可以设置一个contextClassLoader

2.2 Extension ClassLoader(扩展类加载器)

仅含一个实例,由 sun.misc.Launcher$ExtClassLoader实现,负责加载/jre/lib/ext目录或系统属性java.ext.dirs所指定的目录中的所有类库。

2.3 App/System ClassLoader(系统类加载器)

仅含一个实例,由 sun.misc.Launcher$AppClassLoader实现,可通过java.lang.ClassLoader.getSystemClassLoader方法获取。负责加载系统环境变量ClassPath或-cp或系统属性java.class.path 所指定的目录下的类库。他是程序默认的类加载器。开发者可以直接使用这个类加载器

2.4Custom ClassLoader(用户自定义类加载器)

可同时存在多个用户自定义的类加载器。

2.5Context ClassLoader

每个线程实例可以设置一个contextClassLoader,默认为System ClassLoader。可通过Thread.currentThread().setContextClassLoader(ClassLoader)来设置,可通过ClassLoader Thread.currentThread().getContextClassLoader()来获取。每个线程均将Context ClassLoader预先设置为父线程的Context ClassLoader。该类加载器主要用于打破双亲委派模型,容许父类加载器通过子类加载器加载所需的类库。

3. 双亲委派模型

双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器。这里的类加载器之间的父子关系是组合关系来实现来复用父类的代码。

双亲委派模型的工作过程:如果一个类加载器收到类加载的请求,他首先不会自己去尝试加载这个类,而是把这个请求为派给父类加载器去完成。每一层的类加载器都是如此。因此所有的类加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子类才会尝试自己去加载。

使用双亲委派模型的好处在于Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存在在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的Bootstrap ClassLoader进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,那系统中将会出现多个不同的Object类,程序将混乱。因此,如果开发者尝试编写一个与rt.jar类库中重名的Java类,可以正常编译,但是永远无法被加载运行。

双亲委派模型的系统实现
在java.lang.ClassLoader的loadClass()方法中,先检查是否已经被加载过,若没有加载则调用父类加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父加载失败,则抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。

protected synchronized Class<?> loadClass(String name,boolean resolve)throws ClassNotFoundException{    //check the class has been loaded or not    Class c = findLoadedClass(name);        if(c == null){            try{                if(parent != null){                    c = parent.loadClass(name,false);                }else{                    c = findBootstrapClassOrNull(name);                }            }catch(ClassNotFoundException e){                //if throws the exception ,the father can not complete the load            }            if(c == null){                c = findClass(name);            }        }        if(resolve){            resolveClass(c);        }        return c;    }

4. 非双亲委派模型

(待填充)

5. 自定义类加载器的实现

要创建用户自己的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法即可,该方法根据参数指定类的名字,返回对应的Class对象的引用。