文档章节

classLoader

mrliuze
 mrliuze
发布于 2015/04/28 16:59
字数 2537
阅读 6
收藏 0
Java 中类的加载过程 (如 Dog 类):

 

1.       通过类型信息定位Dog.class文件。

2.       载入Dog.class文件,创建相应的Class对象。

3.       执行父类的静态字段定义时初始化语句和父类的静态初始化块。

4.       执行子类的静态字段定义时初始化语句和子类的静态初始化块。

5.       当使用new Dog()方式时,在堆上为Dog对象分配存储空间,并清零分配的存储空间。

6.       执行父类的字段定义时初始化语句和父类的构造函数。

7.       执行子类的字段定义时初始化语句和子类的构造函数。

(参考Java编程思想,部分是自己测试的结果,还没有看到相关资料,这个顺序和C#的顺序好像有比较大的差别)

 

我们知道每个Java类编译后会生成一个.class文件,里面除了存储了类的字节码之外,还存储了和该类对应的Class对象的信息(包含一些用于反射的信息)。在JVM中,通过类加载器(ClassLoader)来实现Class对象的创建。

 

Object类中有publicgetClass()方法,我们可以通过该方法获取与之对应的Class对象。在获取类对应的Class对象后,我们就可以通过newInstance()方法或者通过获取其Constructor的方法来创建该类的实例,也可以通过Class对象获取类或实例相关的更多信息,如方法,字段等,即反射的功能。另外我们也可以通过Class.forName()方法,通过传入类字符串的方式来创建类的实例。(通过Class对象创建类实例和通过forName方法创建类实例是否有联系呢?这两种创建类的实例和直接通过new创建类实例是否也有联系呢?

 

然而Dog.class文件是如何定位的?Dog.class文件是如何被加载并解析的?Class对象又是如何生成的呢?这就是ClassLoader需要解决的问题,也是这篇文章中接下来要讲述的。

 

ClassLoader的工作原理

 

Java内部实现了三种类型的ClassLoaderBootstrap ClassLoaderExtension ClassLoader System ClassLoader。这三个ClassLoader通过parent形成一条单向链,其中Bootstrap处于链尾,而System ClassLoader处于链头。

 

Bootstrap ClassLoader

Bootstrap ClassLoader用于在启动JVM时加载类,以使JVM能正常工作,因而它是用Native代码实现的,最早被创建出来,处于最底层。

Bootstrap ClassLoader将搜索Java核心库(%JAVA_HOME%\jre\lib),如rt.jari18n.jar等,在这些核心库中包含了Java的核心类,即Bootstrap ClassLoader用于加载Java的核心类,包括java.langjava.io等包中的类。

 

Extension ClassLoader

Extension ClassLoader位于链的中间层,它将搜索特定的标准扩展目录。该标准扩展目录在不同的JVM实现中不一定相同,在sunJVM中,它是%JRE_HOME% \lib\ext,具体路径可以通过java.ext.dirs系统属性值获取。该标准扩展目录存在的目的在于扩展和共享,应用程序厂商可以将部分共享库放置于此,而不是各自程序的目录下的多份拷贝。在开发过程中,我们也可以把部分常用的库放置于此,而不必每次都去配置环境。

 

System ClassLoader

System ClassLoader位于链的最顶层,它将搜索CLASSPATH中配置的目录和jar文件。

 

三种ClassLoader的协同工作

既然系统中有三种不同的ClassLoader,那么一个类的加载是用那个ClassLoader呢?在Java中,是通过“代理模型(delegation model)”来决定使用哪个ClassLoader来加载类的(读取.class文件,并创建相应的Class对象)。当一个类需要被加载的时候,系统默认通过System ClassLoader来加载该类;然而System ClassLoader并不立即加载该类,它会将加载的行为代理给其parent去加载,只有当其parent不能加载该类的时,System ClassLoader才去搜索CLASSPATH中的目录和jar文件以加载该类,若找到对应的.class文件,则加载该类,否则抛出ClassNotFoundException。当它将加载的行为代理给Extension ClassLoader并最终代理给Bootstrap ClassLoader的时候,他们也做的是相似的事情。从设计的角度,这其实就是一种职责链模式(Chain of Responsibility Pattern

 

那么Java为什么需要这种代理模型呢?答案是出于安全的考虑。Java是一种安全的编程语言,它会对每个加载的类执行安全检查,但是对Java核心库中的类是不执行安全检查的(即他们是受JVM信赖的)。(什么是安全检查?以我现在所知,就是用户可以通过security manager来控制特定目录的访问权限,如何使用这些控制将会是另一个主题。对安全检查是否还有其他的呢?)那么假如用户可以加载自己编写的和核心库同名的类(如java.lang.Object),那么这些用户编写的类就可以绕过安全检查,从而为一些恶意用户提供了一种破坏的途径。然而在使用这种代理模型后,类的加载首先会代理到Bootstrap ClassLoader中实行加载,如果它发现当前核心类库中可以加载对应的类,系统会加载核心库中的类,而不会加载用户编写的类。

 

但是如果只是这样,还不足以保证类的加载的安全。因为用户完全可以定义自己的ClassLoader,在自己的ClassLoader中破坏这种代理模型,那么用户自己写的java.lang.Object就可以被加载了。为了解决这个问题,在Java中认为两个类名相同的实例,如果加载他们的ClassLoader不同,那么他们是不同的类型,即他们之间不能转换,而且用instanceof操作符返回的是false。这样的话,即使用户加载了自己的java.lang.Object类,它也不是系统认为可以信赖的java.lang.Object,那么它就绕不过安全检查机制。(以上的描述只是我的猜想,我还没有看到相关的资料提到该问题,因而有待验证。)

 

有人说使用这种机制可以实现兼容性的升级,在软件的旧版本中使用某个旧版本的类,在升级的某些新部分中可以使用相同类的新版本,即实现内存中保留一个类的两个版本。我认为这种不应该是一种推荐的做法,在内存中保留一个类的两个版本极易在运行是引起一些莫名其妙的错误。

 

很可惜,我没能找到以上三种ClassLoader的源码,看不了内部的实现。在Java中,它们都是通过ClassLoader这个抽象类来表示的。以下简单的介绍一下ClassLoader内部的成员

(其中Extension ClassLoader的实现类为:sun .misc.Launcher$ExtClassLoader

 

ClassLoader类介绍

ClassLoader类位于java.lang包中,直接继承自Object,是一个抽象类。

 

两个构造函数(protected

一个带parent的参数,一个无参(默认用System ClassLoader作为其parent)。

 

静态方法

public static ClassLoader getSystemClassLoader()

获取System ClassLoader。可以通过设置系统属性:java.system.class.loader的值以修改该函数的返回类型(System.setProperty, System.getProperty)

 

public static URL getSystemResource(String name)

Resource是指数据,如图片、文本、音频等。我的理解Resource就是指在对应ClassLoader搜索路径(目录和jar文件内部)下的任何数据,包括文件和目录。那么该函数就是通过名字(目录名或文件名)返回资源所对应的URL值。(注:名字和返回中的目录分隔符都是用“/”来表示)。该函数的搜索顺序和.class文件的搜索顺序是一样的,所不同的是,用户自定义的ClassLoader可以通过重写public URL findResource(String name)方法来扩展该函数的搜索范围。

 

public static Enumeration<URL> getSystemResources(String name)

getSystemResource,只是它会返回多个URL的集合,用户自定义的ClassLoader可以通过public URL findResources(String name)来扩展该方法。

 

public static InputStream getSystemResourceAsStream(String name)

该函数内部通过调用getSystemResource()方法获取资源的URL地址,然后调用URL.openStream()方法获取资源的InputStream

 

public方法

public Class<?> loadClass(String name) throws ClassNotFoundException

protected synchronized Class<?> loadClass(String name, boolean resolve)

    throws ClassNotFoundException

通过类名加载类,在ClassLoader中,会缓存已加载的Class实例,因而该方法首先查询缓存中是否已经存在该类的Class实例。该方法实现了ClassLoader加载类的算法。JDK的表述,resolve是指链接(link)的意思,但是link又是指什么呢?这个我还清楚。用户自定义子类可以通过findClass()方法来扩展该方法。

 

public URL getResource(String name)

类似静态方法的getResource()方法。

 

public Enumeration<URL> getResources(String name)

类似静态方法的getResources()方法。

 

public final ClassLoader getParent()

如方法名。

 

public InputStream getResourceAsStream(String name)

类似静态方法的getResourceAsStream()方法

 

protected方法

protected URL findResource(String name)

getResource()的扩展方法,提供给子类重写。

 

protected Enumeration<URL> findResources(String name) throws IOException

getResources()的扩展方法,提供给子类重写。

 

protected Class<?> findClass(String name) throws ClassNotFoundException

loadClass()函数的扩展方法,该方法会在所有parent ClassLoader没有找到相应的类定义的时候调用,因而该方法只需要实现当前ClassLoader中需要额外搜索的路径即可。

 

protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,

       ProtectionDomain protectionDomain) throws ClassFormatError

protected final Class<?> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain) throws ClassFormatError

protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError

该方法用于将字节数组转化为相应的Class对象,在该Class对象使用前,该对象必须已经被解析了resolved or linked,同样,什么是解析或链接?)。可以指定ProtectionDomain,也可以使用默认的ProtectionDomain。子类在实现findClass()的时候可以调用该方法以将找到的.class二进制内容转化为Class实例。

本文转载自:http://dlevin.iteye.com/blog/772604

共有 人打赏支持
mrliuze
粉丝 13
博文 154
码字总数 11670
作品 0
浦东
程序员
图解classloader加载class的流程及自定义ClassLoader

java应用环境中不同的class分别由不同的ClassLoader负责加载。 一个jvm中默认的classloader有Bootstrap ClassLoader、Extension ClassLoader、App ClassLoader,分别各司其职: Bootstrap Cl...

zhengguogaun
2013/06/19
0
0
理解 Java ClassLoader 机制

再次阅读这篇文章时,有了更深的体会,特转载之。 理解Java ClassLoader机制 当JVM(Java虚拟机)启动时,会形成由三个类加载器组成的初始类加载器层次结构: bootstrap classloader | exte...

鉴客
2011/07/28
746
5
JDK中提供的ClassLoader

作为一枚java猿,了解类加载器是有必要的,无论是针对面试还是自我学习。 本文从JDK提供的ClassLoader、委托模型以及如何编写自定义的ClassLoader三方面对ClassLoader做一个简要的总结。 1....

281824088
2016/04/08
0
0
Spring源码学习之:ClassLoader学习(3)

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

无信不立
2016/11/07
0
0
ClassLoader 详解及用途(写的不错)

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

yuanhotel
2015/09/21
2.8K
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Confluence 6 使用 Fail2Ban 来限制登录尝试

什么是 Fail2Ban? 我们需要在我们网站中防止密码的暴利破解。Fail2Ban 是一个 Python 的应用来查看日志文件,使用的是正则表达式,同时还可以与Shorewall (或者 iptables)直接工作来来启用...

honeymose
8分钟前
0
0
日期和时间API - 读《Java 8实战》

日期与时间 LocalDate 创建一个LocalDate对象并读取其值 // 根据年月日创建日期LocalDate date1 = LocalDate.of(2014, 3, 18);// 读取System.out.println(date1.getYear()); // 2014Sys...

yysue
8分钟前
0
0
8月15日任务

8月15日任务 Memcached命令行 • telnet 127.0.0.1 11211 • set key2 0 30 2 ab STORED get key2 VALUE key2 0 2 ab END 实例: [root@localhost 02]# telnet 127.0.0.1 11211-bash: te......

寰宇01
20分钟前
0
0
LNMP架构(Nginx访问日志、Nginx日志切割、静态文件不记录日志和过期时间)

Nginx访问日志 1.打开配置文件,搜索log_format vim /usr/local/nginx/conf/nginx.conf 2.访问日志常用变量含义 $remote_addr : 客户端IP(公网IP) $http_x_forwarded_for : 代理服务器的IP ...

蛋黄_Yolks
20分钟前
0
0
lombok 不用再写pojo的getset

java实体类不写get/set方法 1、下载地址https://projectlombok.org/download Myeclipse、eclipse安装lombok Lombok是一种Java实用工具,可以帮助开发人员消除Java的冗长,具体看lombok的官网...

木之下
28分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部