Java类加载器学习总结.doc

加载器概念 类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance方法就可以创建出该类的一个对象。

类加载器分类 JDK 默认提供了如下几种ClassLoader 1、Bootstrp Class Loader Bootstrp加载器是用C语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载JAVA_HOME/jre/lib或者-Xbootclasspath参数指定的路径以及JAVA_HOME/jre/classes中的类,虚拟机出于安全等因素考虑,不会加载JAVA_HOME/jre/lib路径下存在的陌生类,开发者通过将要加载的非JDK自身的类放置到此目录下期待启动类加载器加载是不可能的。

2、ExtClassLoader Bootstrp class loader加载ExtClassLoader,并且将ExtClassLoader的父加载器设置为Bootstrp loader。ExtClassLoader是用Java写的,具体来说就是sun.misc.LauncherExtClassLoader,ExtClassLoader主要加载JAVA_HOME/jre/lib/ext,此路径下的所有classes目录以及java.ext.dirs系统变量指定的路径中类库。

3、AppClassLoader Bootstrp class loader加载完ExtClassLoader后,就会加载AppClassLoader,并且将AppClassLoader的父加载器指定为ExtClassLoader。AppClassLoader也是用Java写成的,它的实现类是sun.misc.LauncherAppClassLoader,另外我们知道ClassLoader中有个getSystemClassLoader方法,此方法返回的正是AppclassLoader。AppClassLoader主要负责加载classpath所指定的位置的类或者是jar文档,它也是Java程序默认的类加载器。

综上所述,它们之间的关系可以通过下图形象的描述 双亲委托模型 Java中ClassLoader的加载采用了双亲委托机制,采用双亲委托机制加载类的时候采用如下的几个步骤 1 当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载时可以直接返回。

2 当前classLoader的缓存中没有找到被加载的类时,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrp ClassLoader。

3 当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回。

线程上下文类加载器 ContextClassLoader是java.lang.Thread类的一个属性,Thread类中的方法 getContextClassLoader和setContextClassLoaderClassLoader cl用来获取和设置线程的上下文类加载器。如果没有通过 setContextClassLoaderClassLoader cl方法进行设置的话,线程将继承其父线程的上下文类加载器。Java 应用运行的初始线程的上下文类加载器是系统类加载器AppClassLoader,在线程中运行的代码可以通过此类加载器来加载类和资源。

双亲委托模型并不能解决 Java 应用开发中会遇到的类加载器的全部问题。Java 提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现。常见的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。这些 SPI 的接口由 Java 核心库来提供,如 JAXP 的 SPI 接口定义包含在 javax.xml.parsers包中(rt.jar)。这些 SPI 的实现代码很可能是作为 Java 应用所依赖的 jar 包被包含进来,可以通过类路径(CLASSPATH)来找到,如实现了 JAXP SPI 的 Apache Xerces所包含的 jar 包。SPI 接口中的代码经常需要加载具体的实现类。

如 JAXP 中的 javax.xml.parsers.DocumentBuilderFactory类中的 newInstance方法用来生成一个新的 DocumentBuilderFactory的实例。这里的实例的真正的类是继承自 javax.xml.parsers.DocumentBuilderFactory,由 SPI 的实现所提供的。如在 Apache Xerces 中,实现的类是 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl。

而问题在于,SPI 的接口是 Java 核心库的一部分,是由引导类加载器BootstrpClassLoader来加载的;
SPI 实现的 Java 类一般是由系统类加载器APPClassLoader来加载的。引导类加载器是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。它也不能代理给系统类加载器,因为它是系统类加载器的祖先类加载器。也就是说,类加载器的双亲委托模型无法解决这个问题。

线程ContextClassLoader正好解决了这个问题。如果不做任何的设置,Java 应用的线程的ContextClassLoader默认就是系统上下文类AppClassLoader加载器。在 SPI 接口的代码中使用线程ContextClassLoader,就可以成功的加载到 SPI 实现的类。线程ContextClassLoader在很多 SPI 的实现中都会用到。

以下是一个简单的Java 应用,用于测试ContextClassLoader。

import javax.xml.parsers.DocumentBuilderFactory; public class TestClassLoader { public static void mainString[] args throws Exception { TestClassLoader test new TestClassLoader; test.testLoad; } private void testLoad throws Exception { // 1 System.out.printlnThread.currentThread;// Thread[main,5,main] // 2 System.out.printlnThread.currentThread.getContextClassLoader;// sun.misc.LauncherAppClassLoader19821f // 3 DocumentBuilderFactory factory DocumentBuilderFactory.newInstance “org.apache.xerces.jaxp.DocumentBuilderFactoryImpl“, null; } } 注释//2显示当前主线程使用的上下文类加载器是AppClassLoader,注释//3由DocumentBuilderFactory负责加载并实例化它的一个实现类,该实现类由Apache Xerces提供,同时在newInstance中传入了一个空的加载器。

public class DocumentBuilderFactory{ public static DocumentBuilderFactory newInstanceString factoryClassName, ClassLoader classLoader{ try { return DocumentBuilderFactory FactoryFinder.newInstancefactoryClassName, classLoader, false; } catch FactoryFinder.ConfigurationError e { throw new FactoryConfigurationErrore.getException, e.getMessage; } } //...... } DocumentBuilderFactory中的newInstance方法将加载的任务交给FactoryFinder来完成。

class FactoryFinder { static Object newInstanceString className, ClassLoader cl, boolean doFallback throws ConfigurationError { try { //使用给定的加载器cl,获取DocumentBuilderFactoryImpl类的Class对象,此处的cl为null Class providerClass getProviderClassclassName, cl, doFallback; //创建DocumentBuilderFactoryImpl的实例 Object instance providerClass.newInstance; } return instance; } catch ClassNotFoundException x { throw new ConfigurationError “Provider “ className “ not found“, x; } catch Exception x { throw new ConfigurationError “Provider “ className “ could not be instantiated “ x, x; } } //...... } 获取DocumentBuilderFactoryImpl实现类的Class对象在getProviderClass方法中完成。

class FactoryFinder { static p