ClassLoader学习笔记ITeye - 众发娱乐

ClassLoader学习笔记ITeye

2019年02月23日11时04分14秒 | 作者: 映之 | 标签: 运用,模块,供给 | 浏览: 1313

针对这篇文章的学习摘抄:原文:http://java.chinaitlab.com/base/804400.html

 

classloader开始为了满意java Applet而开发,将类从长途下载到浏览器中运转,后来在web容器和osgi中更为广泛运用

 

Classloader的效果就是加载编译后的二进制 class 到JVM中。类加载器担任读取 Java 字节代码,并转换成 java.lang.Class 类的一个实例。每个这样的实例用来表明一个 Java 类。经过此实例的 newInstance()办法就能够创立出该类的一个目标。

 

而基本上一切的classloader都是java.lang.ClassLoader的一个实例(bootstrap classloader不是classLoader的子类)。ClassLoader依据一个字符串称号找到相应的字节码生成一个Class实例。

它有几个办法:

getParent() 回来该类加载器的父类加载器。

loadClass(String name) 加载称号为 name 的类,回来的成果是java.lang.Class 类的实例。

findClass(String name) 查找称号为 name 的类,回来的成果是 java.lang.Class 类的实例。

findLoadedClass(String name) 查找称号为 name 的现已被加载过的类,回来的成果是 java.lang.Class 类的实例。

defineClass(String name, byte[] b, int off, int len) 把字节数组 b 中的内容转换成 Java 类,回来的成果是 java.lang.Class 类的实例。这个办法被声明为 final 的。

resolveClass(Class ? c) 链接指定的 Java 类,或许的意思是装入class相关的类。

Java 中的类加载器大致能够分红两类,一类是体系供给的,别的一类则是由 Java 运用开发人员编写的。体系供给的类加载器主要有下面三个:

引导类加载器(bootstrap class loader):它用来加载 Java 的中心库,是用原生代码来完结的,并不承继自 java.lang.ClassLoader。(jre/lib下的几个包)。

扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的完结会供给一个扩展库目录。该类加载器在此目录里边查找并加载 Java 类。(jre/lib/ext下的几个包)

体系类加载器(system class loader):它依据 Java 运用的类途径(CLASSPATH)来加载 Java 类。一般来说,Java 运用的类都是由它来完结加载的。能够经过 ClassLoader.getSystemClassLoader() 来获取它。(java运用程序的加载类)

除了体系供给的类加载器以外,开发人员能够经过承继 java.lang.ClassLoader 类的办法完结自己的类加载器,以满意一些特别的需求。

除了引导类加载器之外,一切的类加载器都有一个父类加载器。类加载器在测验自己去查找某个类的字节代码并界说它时,会先署理给其父类加载器,由父类加载器先去测验加载这个类,顺次类推。首要了解JVM关于java类相同的判别。

jvm发动时,会发动jre/rt.jar里的类加载器:bootstrap classloader,用来加载java中心api;然后发动扩展类加载器ExtClassLoader加载扩展类,终究加载用户程序加载器AppClassLoader。

经过以下代码可知bootstrap加载了那些中心库:

URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();

      for (int i = 0; i urls.length; i++) {

        System.out.println(urls[i].toExternalForm());

      }

咱们不需求在体系特点CLASSPATH中指定这些中心库,因为JVM在发动的时分就主动加载它们了。

经过以下代码可知extensions class loader加载了什么类库:

System.out.println(System.getProperty("java.ext.dirs"));

        ClassLoader extensionClassloader = ClassLoader.getSystemClassLoader().getParent();

        System.out.println("the parent of extension classloader : " + extensionClassloader.getParent());

成果:C:\Program Files\Java\jdk1.6.0_13\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext

 the parent of extension classloader : null

成果阐明:extension classloader是system classloader的parent,而bootstrap classloader是extension classloader的parent,但它不是一个实践的classloader,所认为null。

Java 虚拟机是怎么断定两个 Java 类是相同的?Java 虚拟机不只要看类的全名是否相同,还要看加载此类的类加载器是否相同。只要两者都相同的状况,才认为两个类是相同的。(每个 Java 类都维护着一个指向界说它的类加载器的引证,经过 getClassLoader() 办法就能够获取到此引证。)

了解了这一点之后,就能够了解署理形式的规划动机了。署理形式是为了确保 Java 中心库的类型安全。一切 Java 运用都至少需求引证 java.lang.Object 类,也就是说在运转的时分,java.lang.Object 这个类需求被加载到 Java 虚拟机中。假如这个加载进程由 Java 运用自己的类加载器来完结的话,很或许就存在多个版别的 java.lang.Object 类,而且这些类之间是不兼容的。经过署理形式,关于 Java 中心库的类的加载作业由引导类加载器来一致完结,确保了 Java 运用所运用的都是同一个版别的 Java 中心库的类,是彼此兼容的。

假定一个类被两个classloader来加载,选用署理形式就能够确保中心库中的类是仅有的,都是经过同一个classloader加载的。

不同的类加载器为相同称号的类创立了额定的称号空间。相同称号的类能够并存在 Java 虚拟机中,只需求用不同的类加载器来加载它们即可。不同类加载器加载的类之间是不兼容的,这就相当于在 Java 虚拟机内部创立了一个个彼此阻隔的 Java 类空间。这种技能在许多结构中都被用到。

真实完结类的加载作业是经过调用 defineClass 来完结的;而发动类的加载进程是经过调用 loadClass 来完结的。在 Java 虚拟机判别两个类是否相同的时分,运用的是类的界说加载器。也就是说,哪个类加载器发动类的加载进程并不重要,重要的是终究界说这个类的加载器。一个类的界说加载器是它引证的其它类的初始加载器。

上下文类加载器:

类 java.lang.Thread 中的办法 getContextClassLoader() 和 setContextClassLoader(ClassLoader cl) 用来获取和设置线程的上下文类加载器。Java 运用运转的初始线程的上下文类加载器是体系类加载器。在线程中运转的代码能够经过此类加载器来加载类和资源。

前面说到的类加载器的署理形式并不能处理 Java 运用开发中会遇到的类加载器的悉数问题。如java供给的效劳供给者接口SPI,答应第三方为这些接口供给完结。常见的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。这些 SPI 的接口由 Java 中心库来供给。这些 SPI 的完结代码很或许是作为 Java 运用所依靠的 jar 包被包括进来,能够经过类途径(CLASSPATH)来找到。

问题在于,SPI 的接口是 Java 中心库的一部分,是由引导类加载器来加载的;而SPI 的完结类一般是由体系类加载器来加载的。引导类加载器是无法找到 SPI 的完结类的,因为它只加载 Java 的中心库。它也不能署理给体系类加载器,因为它是体系类加载器的先人类加载器。也就是说,类加载器的署理形式无法处理这个问题。

线程上下文类加载器正好处理了这个问题。假如不做任何的设置,Java 运用的线程的上下文类加载器默许就是体系上下文类加载器。在 SPI 接口的代码中运用线程上下文类加载器,就能够成功的加载到 SPI 完结的类。线程上下文类加载器在许多 SPI 的完结中都会用到。

类加载器与 Web 容器

关于运转在 Java EE 容器中的 Web 运用来说,类加载器的完结办法与一般的 Java 运用有所不同。不同的 Web 容器的完结办法也会有所不同。以 Apache Tomcat 来说,每个 Web 运用都有一个对应的类加载器实例。该类加载器也运用署理形式,所不同的是它是首要测验去加载某个类,假如找不到再署理给父类加载器。这与一般类加载器的次序是相反的。这是 Java Servlet 规范中的引荐做法,其意图是使得 Web 运用自己的类的优先级高于 Web 容器供给的类。这种署理形式的一个破例是:Java 中心库的类是不在查找规模之内的。这也是为了确保 Java 中心库的类型安全。

类加载器与 OSGi

OSGi 是 Java 上的动态模块体系。它为开发人员供给了面向效劳和根据组件的运转环境,并供给规范的办法用来管理软件的生命周期。

OSGi 中的每个模块都有对应的一个类加载器。它担任加载模块自己包括的 Java 包和类。当它需求加载 Java 中心库的类时(以 java 最初的包和类),它会署理给父类加载器(通常是发动类加载器)来完结。当它需求加载所导入的 Java 类时,它会署理给导出此 Java 类的模块来完结加载。模块也能够显式的声明某些 Java 包和类,必须由父类加载器来加载。只需求设置体系特点 org.osgi.framework.bootdelegation 的值即可。

比如阐明:假设有两个模块 bundleA 和 bundleB,它们都有自己对应的类加载器 classLoaderA 和 classLoaderB。在 bundleA 中包括类 com.bundleA.Sample,而且该类被声明为导出的,也就是说能够被其它模块所运用的。bundleB 声明晰导入 bundleA 供给的类 com.bundleA.Sample,并包括一个类 com.bundleB.NewSample 承继自 com.bundleA.Sample。在 bundleB 发动的时分,其类加载器 classLoaderB 需求加载类 com.bundleB.NewSample,进而需求加载类 com.bundleA.Sample。因为 bundleB 声明晰类 com.bundleA.Sample 是导入的,classLoaderB 把加载类 com.bundleA.Sample 的作业署理给导出该类的 bundleA 的类加载器 classLoaderA。classLoaderA 在其模块内部查找类 com.bundleA.Sample 并界说它,所得到的类 com.bundleA.Sample 实例就能够被一切声明导入了此类的模块运用。关于以 java 最初的类,都是由父类加载器来加载的。假如声明晰体系特点 org.osgi.framework.bootdelegation=com.example.core.*,那么关于包 com.example.core 中的类,都是由父类加载器来完结的。

OSGi 模块的这品种加载器结构,使得一个类的不同版别能够共存在 Java 虚拟机中,带来了很大的灵活性。不过它的这种不同,也会给开发人员带来一些费事,特别当模块需求运用第三方供给的库的时分。下面供给几条比较好的主张:

假如一个类库只要一个模块运用,把该类库的 jar 包放在模块中,在 Bundle-ClassPath 中指明即可。

假如一个类库被多个模块共用,能够为这个类库独自的创立一个模块,把其它模块需求用到的 Java 包声明为导出的。其它模块声明导入这些类。

假如类库供给了 SPI 接口,而且使用线程上下文类加载器来加载 SPI 完结的 Java 类,有或许会找不到 Java 类。假如呈现了 NoClassDefFoundError 反常,首要查看当时线程的上下文类加载器是否正确。经过 Thread.currentThread().getContextClassLoader() 就能够得到该类加载器。该类加载器应该是该模块对应的类加载器。假如不是的话,能够首要经过 class.getClassLoader() 来得到模块对应的类加载器,再经过 Thread.currentThread().setContextClassLoader() 来设置当时线程的上下文类加载器。

两个相关文章:

http://www.blogjava.net/lhulcn618/archive/2006/05/25/48230.html

http://lyg5623.blog.163.com/blog/static/5327401120088284439854/

 

版权声明
本文来源于网络,版权归原作者所有,其内容与观点不代表众发娱乐立场。转载文章仅为传播更有价值的信息,如采编人员采编有误或者版权原因,请与我们联系,我们核实后立即修改或删除。

猜您喜欢的文章

阅读排行

  • 1

    Java 动态署理ITeye

    动态,办法,调用
  • 2

    ClassLoader学习笔记ITeye

    运用,模块,供给
  • 3

    java compareTo ComparatorITeye

    字符串,长度,办法
  • 4
  • 5
  • 6
  • 7
  • 8

    Stack的使用方法ITeye

    办法,调用,方式
  • 9

    Java操作HessianITeye

    效劳,运用,客户端
  • 10

    排序算法众发娱乐

    排序,交流,冒泡排序