Java类加载器学习总结.doc_第1页
Java类加载器学习总结.doc_第2页
Java类加载器学习总结.doc_第3页
Java类加载器学习总结.doc_第4页
Java类加载器学习总结.doc_第5页
已阅读5页,还剩7页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

加载器概念类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。类加载器分类JDK 默认提供了如下几种ClassLoader:1、Bootstrp Class LoaderBootstrp加载器是用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.Launcher$ExtClassLoader,ExtClassLoader主要加载%JAVA_HOME%/jre/lib/ext,此路径下的所有classes目录以及java.ext.dirs系统变量指定的路径中类库。3、AppClassLoader Bootstrp class loader加载完ExtClassLoader后,就会加载AppClassLoader,并且将AppClassLoader的父加载器指定为ExtClassLoader。AppClassLoader也是用Java写成的,它的实现类是sun.misc.Launcher$AppClassLoader,另外我们知道ClassLoader中有个getSystemClassLoader方法,此方法返回的正是AppclassLoader。AppClassLoader主要负责加载classpath所指定的位置的类或者是jar文档,它也是Java程序默认的类加载器。综上所述,它们之间的关系可以通过下图形象的描述:双亲委托模型Java中ClassLoader的加载采用了双亲委托机制,采用双亲委托机制加载类的时候采用如下的几个步骤:1 当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载时可以直接返回。2 当前classLoader的缓存中没有找到被加载的类时,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrp ClassLoader。3 当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回。线程上下文类加载器ContextClassLoader是java.lang.Thread类的一个属性,Thread类中的方法 getContextClassLoader()和setContextClassLoader(ClassLoader cl)用来获取和设置线程的上下文类加载器。如果没有通过 setContextClassLoader(ClassLoader 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 main(String args) throws Exception TestClassLoader test = new TestClassLoader();test.testLoad();private void testLoad() throws Exception / 1System.out.println(Thread.currentThread();/ Threadmain,5,main/ 2System.out.println(Thread.currentThread().getContextClassLoader();/ sun.misc.Launcher$AppClassLoader19821f/ 3DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(org.apache.xerces.jaxp.DocumentBuilderFactoryImpl, null);注释/2显示当前主线程使用的上下文类加载器是AppClassLoader,注释/3由DocumentBuilderFactory负责加载并实例化它的一个实现类,该实现类由Apache Xerces提供,同时在newInstance中传入了一个空的加载器。public class DocumentBuilderFactorypublic static DocumentBuilderFactory newInstance(String factoryClassName, ClassLoader classLoader) try return (DocumentBuilderFactory) FactoryFinder.newInstance(factoryClassName, classLoader, false); catch (FactoryFinder.ConfigurationError e) throw new FactoryConfigurationError(e.getException(), e.getMessage(); /.DocumentBuilderFactory中的newInstance方法将加载的任务交给FactoryFinder来完成。class FactoryFinder static Object newInstance(String className, ClassLoader cl, boolean doFallback) throws ConfigurationError try /使用给定的加载器cl,获取DocumentBuilderFactoryImpl类的Class对象,此处的cl为null Class providerClass = getProviderClass(className, 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 private Class getProviderClass(String className, ClassLoader cl, boolean doFallback) throws ClassNotFoundException try if (cl = null) /1 cl = ss.getContextClassLoader(); if (cl = null) throw new ClassNotFoundException(); else /2由上下文类加载器完成实现类的加载 return cl.loadClass(className); else return cl.loadClass(className); catch (ClassNotFoundException e1) /. /.由于cl传入的是一个null值,所以需要从ss对象中获取一个上下文类加载器,那ss对象中的getContextClassLoader方法如何获取一个上下文加载器的呢?class SecuritySupport ClassLoader getContextClassLoader() throws SecurityExceptionreturn (ClassLoader)AccessController.doPrivileged(new PrivilegedAction() public Object run() ClassLoader cl = null; /1 cl = Thread.currentThread().getContextClassLoader(); if (cl = null) cl = ClassLoader.getSystemClassLoader(); return cl; );/.该方法中的其他代码不需要管,由注释/1处可以看出最后return语句返回的cl加载器,就是从当前线程中获得的上下文类加载器。使用myeclipse调试跟踪代码可以看出,当前只有一个mian主线程。主线程中的上下文类加载器默认就是AppClassLoader。方法getProviderClass()中的注释/2处使用AppClassLoader加载器来完成实现类的加载。在注释/2处为什么不使用FactoryFinder.Class.getClassLoader()来完成实现类的加载呢?因为FactoryFinder类位于核心类库中,由Bootstrp类加载器完成加载,而实现类DocumentBuilderFactoryImpl在我们自己的java应用中,在Bootstrp加载器的搜索范围中,找不到该实现类,因此这样的方式无法完成类的加载。另外一个原因,即使将实现类放到Bootstrp加载器的搜索范围中,也是不能完成加载,因为Bootstrp只负责加载固定的核心类库,其他的字节码文件一律都不加载。正常的双亲委派模型中,下层的类加载器可以使用上层父加载器加载的对象,但是上层父类的加载器不可能使用子类加载的对象(因为,正常情况下加载器只会向上搜索被加载的类,不会向下搜索)。而有些时候程序的确需要上层调用下层,这时候就需要线程上下文类加载器来处理,以上就是这样的一个例子。自定义类加载器虽然在绝大多数情况下,系统默认提供的类加载器实现已经可以满足需求。但是在某些情况下,您还是需要为应用开发出自己的类加载器。比如您的应用通过网络来传输 Java 类的字节代码,为了保证安全性,这些字节代码经过了加密处理。这个时候您就需要自己的类加载器来从某个网络地址上读取加密后的字节代码,接着进行解密和验证,最后定义出要在 Java 虚拟机中运行的类来。下面将通过具体的实例来说明类加载器的开发。文件系统类加载器第一个类加载器用来加载存储在文件系统上的 Java 字节代码。完整的实现下所示。public class FileSystemClassLoader extends ClassLoader private String rootDir;public FileSystemClassLoader(String rootDir) this.rootDir = rootDir;protected Class findClass(String name) throws ClassNotFoundException byte classData = getClassData(name);if (classData = null) throw new ClassNotFoundException(); else return defineClass(name, classData, 0, classData.length);private byte getClassData(String className) String path = classNameToPath(className);try InputStream ins = new FileInputStream(path);ByteArrayOutputStream baos = new ByteArrayOutputStream();int bufferSize = 4096;byte buffer = new bytebufferSize;int bytesNumRead = 0;while (bytesNumRead = ins.read(buffer) != -1) baos.write(buffer, 0, bytesNumRead);return baos.toByteArray(); catch (IOException e) e.printStackTrace();return null;private String classNameToPath(String className) return rootDir + File.separatorChar+ className.replace(., File.separatorChar) + .class;类 FileSystemClassLoader继承自类 java.lang.ClassLoader。一般来说,自己开发的类加载器只需要

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论