ClassLoader

 1.类加载器概述
类加载器是一个对象,是负责加载类.在JVM是通过类加载器的调用LoadClass方法加载类对象.
类加载器结构:
1. 引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的[null]
2. 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类[ExtClassLoader]
3. 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader() 来获取它[AppClassLoader]
2.类加载器的工作流程

当classLoader有类需要载入时,先让其parent查找载入,如果parent找不到,再由自己搜索路径进行载入。ClassLoader在运行期会以父/子的层次结构存在,每个classLoader 实例都有其父ClassLoader的引用,而父ClassLoader并没有持有子ClassLoader的引用,从而形成一条单向链,当一个类装载请求提交到某个ClassLoader时,默认的类装载过程如下:
1. 检查这个类有没有被装载过,如果已经装载过,返回
2. 调用父ClassLoader去装载类,如果装载成功返回.
3. 调用自身的装载类方法,如果装载成功则返回
4. 如查都没有成功,抛出ClassNotFoundException.
简单说,当ClassLoader链上的某一ClassLoader收到类装载请求时,会按顺序向上询问其所有父节点,直到boot classLoader.任何一个节点成功受理了此请求,则返回,如果所有父节点都不能受理,这个时候才由请求的ClassLoader自身来装载这个类,如果仍不能装载,则抛出异常.
3.Class的类加载器到底是哪个.
类加载器在其应用场景的不同又可以分为如下类加载器:
 1. 系统 ClassLoader
 2. 调用者 ClassLoader
 3. 线程上下文ClassLoader
这些类加载器主要是用于动态加载资源,也可以解决架包的重复问题.
调用者类加载器是指当前所在的类装载时所使用的ClassLoader,它可能是SystemClassLoader, 也可能是一个自定义的ClassLoader.可以通过getClass().getClassLoader()来得到Caller ClassLoader.例如,存在类A,是被AClassLoader所加载,A.class.getClassloader()为AClassLoader的实例,它就是A.class的Caller Classloader.
如果在A类中new一个B类,那么B类的类加载器就一定是AClassLoader吗。答案是错的。因为new一个对象,loadClass(B.class)可能在其父ClassLoader中就已经完成.
决定一个类的类加载器是defineClass,而判断两个类是否为同一对象的标准里面有一条是类加载器必须为相同.
现在有一个问题,如何使用指定的ClassLoader去完成类和资源的加载呢,或者说,当需要去实例化一个调用者ClassLoader和它的父ClassLoader都不能加载的类时,怎么办.
一个典型的一例子是Jaxp,当使用xerces的Sax实现时,我们首先需要通过rt.jar中的java.xml.parsers.SaxparserFactory.getinstance()得到xeceImpl.jar中的org.apache.xerces.jaxp.SAXParserFactory.Impl的实例,由于Jaxp的框架接口的类位于Java_hom/lib/rt.jar中,由bootStrap ClassLoader装载,处于ClassLoader层次结构中的最顶层,而xecesImpl.jar由低怪的ClassLoader装载,也就是说SaxParserFactoryImpl是在SaxParserFactory中实例化的,如前所述,使用SaxParserFactory的CallerClassLoader(boot)是完成不了这个任务的.这里我们需要理解下线程上下文ClassLoader.

线程上下文ClassLoader
. 每一个线程都有一个关联的上下文ClassLoader.如果使用new Thread()方式生成新的线程,新线程将继承其父线程的上下文ClassLoader.如果程序对线程上下文ClassLoader没有任何改动的话,程序的所有线程将都使用System ClassLoader作为上下文ClassLoader.当使用Thread.currentThread().setContextClassLoader(classLoader)时,线程上下文ClassLoader就变成了指定的ClassLoader了。此时,在本线程的任意一处地方,调用Thread.currentThread().getContextClassLoader().都可以得到前面设置的ClassLoader.
一个线程来了,第一件事情便是设置ClassLoader来设置线程上下文类加载器,模块内部都使用线程上下文类加载器[比如某一接口处于AClassLoader,我设置AClassLoader为线程上下文类加载器,那么我通过getContextClassLoader就可以得到A类的类加载器,以后使用A类的类加载器,这样就可以统一调用了]
(有人可能会问了,我总不能每加载一个类,都使用上下文线程去加载吧,我笑了,其实这大可不必,只要你的类是你私有的[在当前类加载器父级加载器未加载过],就不需要重新加载)

4.JVM工作流程
Class Loader 加载流程
 Jvm 建立=>初始化工作=>产生第一个ClassLoader,即boot
 Boot ClassLoader在sum.misc.Launcher类里面的ExtClassLoader,并设置其Parent为Boot.
 Boot ClassLoader载入sun.misc.Launcher$AppClassLoader,设定其parent为ExtClassLoader(但是AppClassLoader也是boot所载入)
 AppClassLoader载入各个xx.class,xx.class也有可能被ExtClassLoader或者boot载入.
 自定义的ClassLoader的getparent()是AppClassLoader.parent和他的加载器没有关系.
 ExtClassLoader和AppClassLoader都是URLClassLoader的子类。
 AppClassLoader的URL是由系统参数java.class.path取出的字符串决定,而java.class.path由运行机制java.exe时的-cp或-classpath或CLASSPATH环境变量决定
 ExtClassLoader查找的url是系统变量java.ext.dirs,java.ext.dirs默认为jdk\jre\lib\ext
 Bootstrap loader的查找url是sun.boot.class.path
5.独立应用的类加载器
由于系统类加载器是JVM最后创建的类加载器,这样代码只会适应于简单命令行启动的程序。一旦代码移植到EJB、Web应用或者Java Web Start应用程序中,程序肯定不能正确执行。
因此一般只有两种选择,当前类加载器和线程上下文类加载器。当前类加载器是指当前方法所在类的加载器。这个类加载器是运行时类解析使用的加载器,Class.forName(String)和Class.getResource(String)也使用该类加载器。代码中X.class的写法使用的类加载器也是这个类加载器。
Web应用和Java企业级应用中,应用服务器经常要使用复杂的类加载器结构来实现JNDI(Java命名和目录接口)、线程池、组件热部署等功能,因此理解这一点尤其重要。

通常JVM中的类加载器是按照层次结构组织的,目的是每个类加载器(除了启动整个JVM的原初类加载器)都有一个父类加载器。当类加载请求到来时,类加载器通常首先将请求代理给父类加载器。只有当父类加载器失败后,它才试图按照自己的算法查找并定义当前类。[如何覆盖父类的加载机制] 

你可能感兴趣的:(S)