文档章节

ClassLoader详解及用途

aminqiao
 aminqiao
发布于 2014/06/04 17:25
字数 2786
阅读 1816
收藏 21
点赞 0
评论 0

ClassLoader主要对类的请求提供服务,当JVM需要某类时,它根据名称向ClassLoader要求这个类,然后由ClassLoader返回 这个类的class对象。

 1.1 几个相关概念ClassLoader负责载入系统的所有Resources(Class,文件,来自网络的字节流 等),通过ClassLoader从而将资源载入JVM 
每个class都有一个reference,指向自己的ClassLoader。Class.getClassLoader() 
array的ClassLoader就是其元素的ClassLoader,若是基本数据类型,则这个array没有ClassLoader 
1.2 主要方法和工作过程Java1.1及从前版本中,ClassLoader主要方法: 
Class loadClass( String name, boolean resolve ); ClassLoader.loadClass() 是 ClassLoader 的入口点 
defineClass 方法是 ClassLoader 的主要诀窍。该方法接受由原始字节组成的数组并把它转换成 Class 对象。原始数组包含如从文件系统或网络装入的数据。 
findSystemClass 方 法从本地文件系统装入文件。它在本地文件系统中寻找类文件,如果存在,就使用 defineClass 将原始字节转换成 Class 对象,以将该文件 转换成类。当运行 Java 应用程序时,这是 JVM 正常装入类的缺省机制。 
resolveClass可以不完全地(不带解析)装入类,也可以完全地(带解析)装入类。当编写我们自己的 loadClass 时,可以调用 resolveClass,这取决于 loadClass 的 resolve 参数的值 
findLoadedClass 充当一个缓存:当请求 loadClass 装入类时,它调用该方法来查看 ClassLoader 是否已装入这个类,这样可以避免重新装入已存在类所造成的麻烦。应首先调用该方法 
一般load方法过程如下: 

调用 findLoadedClass 来查看是否存在已装入的类。 
如果没有,那么采用某种特殊的神奇方式来获取原始字节。(通过IO从文件系统,来自网络的字节流等) 
如果已有原始字节,调用 defineClass 将它们转换成 Class 对象。 
如果没有原始字节,然后调用 findSystemClass 查看是否从本地文件系统获取类。 
如果 resolve 参数是 true,那么调用 resolveClass 解析 Class 对象。 
如果还没有类,返回 ClassNotFoundException。 
否则,将类返回给调用程序。 
1.3 委托模型自从JDK1.2以后,ClassLoader做了改进,使用了委托模型,所有系统中的ClassLoader组成一棵树,ClassLoader在载入类库时先让Parent寻找,Parent找不到才自己找。 
JVM 在运行时会产生三个ClassLoader,Bootstrap ClassLoader、Extension ClassLoader和 App ClassLoader。其中,Bootstrap ClassLoader是用C++编写的,在Java中看不到它,是null。它用来加载核 心类库,就是在lib下的类库,Extension ClassLoader加载lib/ext下的类库,App ClassLoader加载 Classpath里的类库,三者的关系为:App ClassLoader的Parent是Extension ClassLoader,而 Extension ClassLoader的Parent为Bootstrap ClassLoader。加载一个类时,首先BootStrap进行寻 找,找不到再由Extension ClassLoader寻找,最后才是App ClassLoader。 

将 ClassLoader设计成委托模型的一个重要原因是出于安全考虑,比如在Applet中,如果编写了一个java.lang.String类并具有破 坏性。假如不采用这种委托机制,就会将这个具有破坏性的String加载到了用户机器上,导致破坏用户安全。但采用这种委托机制则不会出现这种情况。因为 要加载java.lang.String类时,系统最终会由Bootstrap进行加载,这个具有破坏性的String永远没有机会加载。 

委托模型还带来了一些问题,在某些情况下会产生混淆,如下是Tomcat的ClassLoader结构图: 

                Bootstrap
                  |
                System
                  |
                Common
                /    
            Catalina  Shared
                      /    
                   Webapp1  Webapp2 ...

由 Common 类 装入器装入的类决不能(根据名称)直接访问由 Web 应用程序装入的类。使这些类联系在一起的唯一方法是通过使用这两个类集都可见的接口。在这个例子 中,就是包含由 Java servlet 实现的 javax.servlet.Servlet。 
如果在lib或者lib/ext等类库有 与应用中同样的类,那么应用中的类将无法被载入。通常在jdk新版本出现有类库移动时会出现问题,例如最初我们使用自己的xml解析器,而在jdk1.4 中xml解析器变成标准类库,load的优先级也高于我们自己的xml解析器,我们自己的xml解析器永远无法找到,将可能导致我们的应用无法运行。 

相同的类,不同的ClassLoader,将导致ClassCastException异常 

1.4 线 程中的ClassLoader每个运行中的线程都有一个成员contextClassLoader,用来在运行时动态地载入其它类,可以使用方法 Thread.currentThread().setContextClassLoader(...);更改当前线程的 contextClassLoader,来改变其载入类的行为;也可以通过方法 Thread.currentThread().getContextClassLoader()来获得当前线程的ClassLoader。 
实际上,在Java应用中所有程序都运行在线程里,如果在程序中没有手工设置过ClassLoader,对于一般的java类如下两种方法获得的ClassLoader通常都是同一个 

this.getClass.getClassLoader(); 
Thread.currentThread().getContextClassLoader(); 
方 法一得到的Classloader是静态的,表明类的载入者是谁;方法二得到的Classloader是动态的,谁执行(某个线程),就是那个执行者的 Classloader。对于单例模式的类,静态类等,载入一次后,这个实例会被很多程序(线程)调用,对于这些类,载入的Classloader和执行 线程的Classloader通常都不同。 

1.5 Web应用中的ClassLoader回到上面的例子,在Tomcat 里,WebApp的ClassLoader的工作原理有点不同,它先试图自己载入类(在ContextPath/WEB-INF/...中载入类),如果 无法载入,再请求父ClassLoader完成。 
由此可得: 

对于WEB APP线程,它的contextClassLoader是WebAppClassLoader 
对于Tomcat Server线程,它的contextClassLoader是CatalinaClassLoader 
1.6 获得ClassLoader的几种方法可以通过如下3种方法得到ClassLoader 
this.getClass.getClassLoader(); // 使用当前类的ClassLoader 
Thread.currentThread().getContextClassLoader(); // 使用当前线程的ClassLoader 
ClassLoader.getSystemClassLoader(); // 使 用系统ClassLoader,即系统的入口点所使用的ClassLoader。(注意,system ClassLoader与根 ClassLoader并不一样。JVM下system ClassLoader通常为App ClassLoader) 
1.7 几种扩展应用用户定制自己的ClassLoader可以实现以下的一些应用 
安全性。类进入JVM之前先经过ClassLoader,所以可以在这边检查是否有正确的数字签名等 
加密。java字节码很容易被反编译,通过定制ClassLoader使得字节码先加密防止别人下载后反编译,这里的ClassLoader相当于一个动态的解码器 
归档。可能为了节省网络资源,对自己的代码做一些特殊的归档,然后用定制的ClassLoader来解档 
自展开程序。把java应用程序编译成单个可执行类文件,这个文件包含压缩的和加密的类文件数据,同时有一个固定的ClassLoader,当程序运行时它在内存中完全自行解开,无需先安装 
动态生成。可以生成应用其他还未生成类的类,实时创建整个类并可在任何时刻引入JVM 
2.0 资源载入
所 有资源都通过ClassLoader载入到JVM里,那么在载入资源时当然可以使用ClassLoader,只是对于不同的资源还可以使用一些别的方式载 入,例如对于类可以直接new,对于文件可以直接做IO等。 2.1 载入类的几种方法假设有类A和类B,A在方法amethod里需要实例化B,可能的 方法有3种。对于载入类的情况,用户需要知道B类的完整名字(包括包名,例如"com.rain.B") 
1. 使用Class静态方法 Class.forName 

    Class cls = Class.forName("com.rain.B");
    B b = (B)cls.newInstance();

2. 使用ClassLoader 
    /* Step 1. Get ClassLoader */
    ClassLoader cl; // 如何获得ClassLoader参考1.6

    /* Step 2. Load the class */
    Class cls = cl.loadClass("com.rain.B"); // 使用第一步得到的ClassLoader来载入B
    
    /* Step 3. new instance */
    B b = (B)cls.newInstance(); // 有B的类得到一个B的实例

3. 直接new 
    B b = new B();

2.2 文 件载入(例如配置文件等)假设在com.rain.A类里想读取文件夹 /com/rain/config 里的文件sys.properties,读取 文件可以通过绝对路径或相对路径,绝对路径很简单,在Windows下以盘号开始,在Unix下以"/"开始 
对于相对路径,其相对值是相对于ClassLoader的,因为ClassLoader是一棵树,所以这个相对路径和ClassLoader树上的任何一个ClassLoader相对比较后可以找到文件,那么文件就可以找到,当然,读取文件也使用委托模型 

1. 直接IO 

/**
 * 假设当前位置是 "C:/test",通过执行如下命令来运行A "java com.rain.A"
 * 1. 在程序里可以使用绝对路径,Windows下的绝对路径以盘号开始,Unix下以"/"开始
 * 2. 也可以使用相对路径,相对路径前面没有"/"
 * 因为我们在 "C:/test" 目录下执行程序,程序入口点是"C:/test",相对路径就
 * 是 "com/rain/config/sys.properties"
 * (例子中,当前程序的ClassLoader是App ClassLoader,system ClassLoader = 当前的
 * 程序的ClassLoader,入口点是"C:/test")
 * 对于ClassLoader树,如果文件在jdk lib下,如果文件在jdk lib/ext下,如果文件在环境变量里,
 * 都可以通过相对路径"sys.properties"找到,lib下的文件最先被找到
 */
File f = new File("C:/test/com/rain/config/sys.properties"); // 使用绝对路径
//File f = new File("com/rain/config/sys.properties"); // 使用相对路径
InputStream is = new FileInputStream(f);

如果是配置文件,可以通过java.util.Properties.load(is)将内容读到Properties里,Properties默认认为is的编码是ISO-8859-1,如果配置文件是非英文的,可能出现乱码问题。 
2. 使用ClassLoader 

/**
 * 因为有3种方法得到ClassLoader,对应有如下3种方法读取文件
 * 使用的路径是相对于这个ClassLoader的那个点的相对路径,此处只能使用相对路径
 */
InputStream is = null;
is = this.getClass().getClassLoader().getResourceAsStream(
       "com/rain/config/sys.properties"); //方法1
//is = Thread.currentThread().getContextClassLoader().getResourceAsStream(
       "com/rain/config/sys.properties"); //方法2
//is = ClassLoader.getSystemResourceAsStream("com/rain/config/sys.properties"); //方法3

如果是配置文件,可以通过java.util.Properties.load(is)将内容读到Properties里,这里要注意编码问题。 
3. 使用ResourceBundle 

    ResourceBundle bundle = ResourceBundle.getBoundle("com.rain.config.sys");

这种用法通常用来载入用户的配置文件,关于ResourceBunlde更详细的用法请参考其他文档 
总结:有如下3种途径来载入文件 

    1. 绝对路径 ---> IO
    2. 相对路径 ---> IO
                ---> ClassLoader
    3. 资源文件 ---> ResourceBundle

2.3 如何在web应用里载入资源在web应用里当然也可以使用ClassLoader来载入资源,但更常用的情况是使用ServletContext,如下是web目录结构 
    ContextRoot
       |- JSP、HTML、Image等各种文件
        |- [WEB-INF]
              |- web.xml
              |- [lib] Web用到的JAR文件
                |- [classes] 类文件

用户程序通常在classes目录下,如果想读取classes目录里的文件,可以使用ClassLoader,如果想读取其他的文件,一般使用ServletContext.getResource() 
如 果使用ServletContext.getResource(path)方法,路径必须以"/"开始,路径被解释成相对于ContextRoot的路 径,此处载入文件的方法和ClassLoader不同,举例"/WEB-INF/web.xml","/download /WebExAgent.rar"

本文转载自:

共有 人打赏支持
aminqiao
粉丝 7
博文 30
码字总数 1305
作品 0
广州
ClassLoader 详解及用途(写的不错)

ClassLoader主要对类的请求提供服务,当JVM需要某类时,它根据名称向ClassLoader要求这个类,然后由ClassLoader返回这个类的class对象。 1.1 几个相关概念ClassLoader负责载入系统的所有Res...

yuanhotel ⋅ 2015/09/21 ⋅ 0

Java程序员从笨鸟到菜鸟之(九十八)深入java虚拟机(七)深入源码看java类加载器ClassLoader

欢迎阅读本专题的其他博客: 深入java虚拟机(一)——java虚拟机底层结构详解 深入java虚拟机(二)——类的生命周期(上)类的加载和连接 深入java虚拟机(三)——类的生命周期(下)类的初...

长平狐 ⋅ 2012/11/12 ⋅ 0

Java 一个特殊的类 ServiceLoader 详解

ServiceLoader与ClassLoader是Java中2个即相互区别又相互联系的加载器.JVM利用ClassLoader将类载入内存,这是一个类声明周期的第一步(一个java类的完整的生命周期会经历加载、连接、初始化、...

Only_小白 ⋅ 2016/04/28 ⋅ 0

代理模式&动态代理

动态代理的用途: 动态代理的用途与装饰模式很相似,就是为了对某个对象进行增强。所有使用装饰者模式的案例都可以使用动态代理来替换,动态代理可以更好的解耦合 增强有3个手段 1. 继承 被增强...

wall--e ⋅ 2016/04/14 ⋅ 0

Redkale 技术详解 01 -- 双亲委托模型

Redkale 技术详解 01 -- 双亲委托模型 Redkale 里大量使用了双亲委托模型,序列化的ConvertFactory、依赖注入的ResourceFactory、服务管理的WatchFactory均采用双亲委托模型。用于优先加载自...

Redkale ⋅ 2016/02/17 ⋅ 1

Java基础-Java编译详解

Java基础-Java编译详解 Java源代码编译过程 Java代码编译是由Java源码编译器来完成,流程图如下所示: Java字节码的执行是由JVM执行引擎来完成,流程图如下所示: Java代码编译和执行的整个过...

xjt2016 ⋅ 2016/12/27 ⋅ 0

初探Java类加载机制

初探Java类加载机制  一、在jdk1.2以后,类加载是通过委托来完成的,这意味着如果 ClassLoader 不能找到类,它会请求父代 ClassLoader 来执行此项任务,所有 ClassLoaders 的根是系统 Clas...

疯狂的小黑 ⋅ 2016/10/27 ⋅ 0

关于Java的ClassLoader

关于理论不多说,本人不善于理论。这里有一篇文章讲ClassLoad的,讲的很详细:Java ClassLoad详解 我这里所讲的是,我实际开发中曾经遇到一个问题,是这样的,以前有个需求是每天爬取多个日报...

小黑放大 ⋅ 2015/03/26 ⋅ 0

用ireport做报表的经验

20151127: 晚上下了本电子书,ireport开发详解,按他说的,官网下载了jaspereport-lib后,把disk目录下jar都拷贝到lib下,靠,不停的报错,根据报错依次在ireport下找到了对应的jar,commo...

wrean2013 ⋅ 2015/11/28 ⋅ 0

ClassCastException深入分析

ClassCastException是JVM在检测到两个类型间转换不兼容时引发的运行时异常。此类错误通常会终止用户请求。在执行任何子系统的应用程序代码时都有可能发生ClassCastException异常。通过转换,...

pczhangtl ⋅ 2014/08/07 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

从 Confluence 5.3 及其早期版本中恢复空间

如果你需要从 Confluence 5.3 及其早期版本中的导出文件恢复到晚于 Confluence 5.3 的 Confluence 中的话。你可以使用临时的 Confluence 空间安装,然后将这个 Confluence 安装实例升级到你现...

honeymose ⋅ 今天 ⋅ 0

用ZBLOG2.3博客写读书笔记网站能创造今日头条的辉煌吗?

最近两年,著名的自媒体网站今日头条可以说是火得一塌糊涂,虽然从目前来看也遇到了一点瓶颈,毕竟发展到了一定的规模,继续增长就更加难了,但如今的今日头条规模和流量已经非常大了。 我们...

原创小博客 ⋅ 今天 ⋅ 0

MyBatis四大核心概念

本文讲解 MyBatis 四大核心概念(SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession、Mapper)。 MyBatis 作为互联网数据库映射工具界的“上古神器”,训有四大“神兽”,谓之:Sql...

waylau ⋅ 今天 ⋅ 0

以太坊java开发包web3j简介

web3j(org.web3j)是Java版本的以太坊JSON RPC接口协议封装实现,如果需要将你的Java应用或安卓应用接入以太坊,或者希望用java开发一个钱包应用,那么用web3j就对了。 web3j的功能相当完整...

汇智网教程 ⋅ 今天 ⋅ 0

2个线程交替打印100以内的数字

重点提示: 线程的本质上只是一个壳子,真正的逻辑其实在“竞态条件”中。 举个例子,比如本题中的打印,那么在竞态条件中,我只需要一个方法即可; 假如我的需求是2个线程,一个+1,一个-1,...

Germmy ⋅ 今天 ⋅ 0

Springboot2 之 Spring Data Redis 实现消息队列——发布/订阅模式

一般来说,消息队列有两种场景,一种是发布者订阅者模式,一种是生产者消费者模式,这里利用redis消息“发布/订阅”来简单实现订阅者模式。 实现之前先过过 redis 发布订阅的一些基础概念和操...

Simonton ⋅ 今天 ⋅ 0

error:Could not find gradle

一.更新Android Studio后打开Project,报如下错误: Error: Could not find com.android.tools.build:gradle:2.2.1. Searched in the following locations: file:/D:/software/android/andro......

Yao--靠自己 ⋅ 昨天 ⋅ 0

Spring boot 项目打包及引入本地jar包

Spring Boot 项目打包以及引入本地Jar包 [TOC] 上篇文章提到 Maven 项目添加本地jar包的三种方式 ,本篇文章记录下在实际项目中的应用。 spring boot 打包方式 我们知道,传统应用可以将程序...

Os_yxguang ⋅ 昨天 ⋅ 0

常见数据结构(二)-树(二叉树,红黑树,B树)

本文介绍数据结构中几种常见的树:二分查找树,2-3树,红黑树,B树 写在前面 本文所有图片均截图自coursera上普林斯顿的课程《Algorithms, Part I》中的Slides 相关命题的证明可参考《算法(第...

浮躁的码农 ⋅ 昨天 ⋅ 0

android -------- 混淆打包报错 (warning - InnerClass ...)

最近做Android混淆打包遇到一些问题,Android Sdutio 3.1 版本打包的 错误如下: Android studio warning - InnerClass annotations are missing corresponding EnclosingMember annotation......

切切歆语 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部