- 浏览: 25919 次
文章分类
最新评论
ClassLoader一个经常出现又让很多人望而却步的词,本文将试图以最浅显易懂的方式来讲解 ClassLoader,希望能对不了解该机制的朋友起到一点点作用。
要深入了解ClassLoader,首先就要知道ClassLoader是用来干什么的,顾名思义,它就是用来加载Class文件到JVM,以供程序使用的。我们知道,java程序可以动态加载类定义,而这个动态加载的机制就是通过ClassLoader来实现的,所以可想而知ClassLoader的重要性如何。
看到这里,可能有的朋友会想到一个问题,那就是既然ClassLoader是用来加载类到JVM中的,那么ClassLoader又是如何被加载呢?难道它不是java的类?
没有错,在这里确实有一个ClassLoader不是用java语言所编写的,而是JVM实现的一部分,这个ClassLoader就是bootstrap classloader(启动类加载器),这个ClassLoader在JVM运行的时候加载java核心的API以满足java程序最基本的需求,其中就包括用户定义的ClassLoader,这里所谓的用户定义是指通过java程序实现的ClassLoader,一个是ExtClassLoader,这个ClassLoader是用来加载java的扩展API的,也就是/lib/ext中的类,一个是AppClassLoader,这个ClassLoader是用来加载用户机器上CLASSPATH设置目录中的Class的,通常在没有指定ClassLoader的情况下,程序员自定义的类就由该ClassLoader进行加载。
当运行一个程序的时候,JVM启动,运行bootstrap classloader,该ClassLoader加载java核心API(ExtClassLoader和AppClassLoader也在此时被加载),然后调用ExtClassLoader加载扩展API,最后AppClassLoader加载CLASSPATH目录下定义的Class,这就是一个程序最基本的加载流程。
上面大概讲解了一下ClassLoader的作用以及一个最基本的加载流程,接下来将讲解一下ClassLoader加载的方式,这里就不得不讲一下ClassLoader在这里使用了双亲委托模式进行类加载。
每一个自定义ClassLoader都必须继承ClassLoader这个抽象类,而每个ClassLoader都会有一个parent ClassLoader,我们可以看一下ClassLoader这个抽象类中有一个getParent()方法,这个方法用来返回当前ClassLoader的parent,注意,这个parent不是指的被继承的类,而是在实例化该ClassLoader时指定的一个ClassLoader,如果这个parent为null,那么就默认该ClassLoader的parent是bootstrap classloader,这个parent有什么用呢?
我们可以考虑这样一种情况,假设我们自定义了一个ClientDefClassLoader,我们使用这个自定义的ClassLoader加载java.lang.String,那么这里String是否会被这个ClassLoader加载呢?事实上java.lang.String这个类并不是被这个ClientDefClassLoader加载,而是由bootstrap classloader进行加载,为什么会这样?实际上这就是双亲委托模式的原因,因为在任何一个自定义ClassLoader加载一个类之前,它都会先委托它的父亲ClassLoader进行加载,只有当父亲ClassLoader无法加载成功后,才会由自己加载,在上面这个例子里,因为java.lang.String是属于java核心API的一个类,所以当使用ClientDefClassLoader加载它的时候,该ClassLoader会先委托它的父亲ClassLoader进行加载,上面讲过,当ClassLoader的parent为null时,ClassLoader的parent就是bootstrap classloader,所以在ClassLoader的最顶层就是bootstrap classloader,因此最终委托到bootstrap classloader的时候,bootstrap classloader就会返回String的Class。
我们来看一下ClassLoader中的一段源代码:
- protected synchronized Class loadClass(String name, boolean resolve)
- throws ClassNotFoundException
- {
- // 首先检查该name指定的class是否有被加载
- Class c = findLoadedClass(name);
- if (c == null) {
- try {
- if (parent != null) {
- //如果parent不为null,则调用parent的loadClass进行加载
- = parent.loadClass(name, false);
- } else {
- //parent为null,则调用BootstrapClassLoader进行加载
- c = findBootstrapClass0(name);
- }
- } catch (ClassNotFoundException e) {
- //如果仍然无法加载成功,则调用自身的findClass进行加载
- c = findClass(name);
- }
- }
- if (resolve) {
- resolveClass(c);
- }
- return c;
- }
protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { // 首先检查该name指定的class是否有被加载 Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { //如果parent不为null,则调用parent的loadClass进行加载 c = parent.loadClass(name, false); } else { //parent为null,则调用BootstrapClassLoader进行加载 c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { //如果仍然无法加载成功,则调用自身的findClass进行加载 c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }
从上面一段代码中,我们可以看出一个类加载的大概过程与之前我所举的例子是一样的,而我们要实现一个自定义类的时候,只需要实现findClass方法即可。
为什么要使用这种双亲委托模式呢?
第一个原因就是因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。
第二个原因就是考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时被加载,所以用户自定义类是无法加载一个自定义的ClassLoader。
上面对ClassLoader的加载机制进行了大概的介绍,接下来不得不在此讲解一下另外一个和ClassLoader相关的类,那就是Class类,每个被ClassLoader加载的class文件,最终都会以Class类的实例被程序员引用,我们可以把Class类当作是普通类的一个模板,JVM根据这个模板生成对应的实例,最终被程序员所使用。
我们看到在Class类中有个静态方法forName,这个方法和ClassLoader中的loadClass方法的目的一样,都是用来加载class的,但是两者在作用上却有所区别。
Class<?> loadClass(String name)
Class<?> loadClass(String name, boolean resolve)
我们看到上面两个方法声明,第二个方法的第二个参数是用于设置加载类的时候是否连接该类,true就连接,否则就不连接。
说到连接,不得不在此做一下解释,在JVM加载类的时候,需要经过三个步骤,装载、连接、初始化。装载就是找到相应的class文件,读入JVM,初始化就不用说了,最主要就说说连接。
连接分三步,第一步是验证class是否符合规格,第二步是准备,就是为类变量分配内存同时设置默认初始值,第三步就是解释,而这步就是可选的,根据上面loadClass方法的第二个参数来判定是否需要解释,所谓的解释根据《深入JVM》这本书的定义就是根据类中的符号引用查找相应的实体,再把符号引用替换成一个直接引用的过程。有点深奥吧,呵呵,在此就不多做解释了,想具体了解就翻翻《深入JVM吧》,呵呵,再这样一步步解释下去,那就不知道什么时候才能解释得完了。
我们再来看看那个两个参数的loadClass方法,在JAVA API 文档中,该方法的定义是protected,那也就是说该方法是被保护的,而用户真正应该使用的方法是一个参数的那个,一个参数的loadclass方法实际上就是调用了两个参数的方法,而第二个参数默认为false,因此在这里可以看出通过loadClass加载类实际上就是加载的时候并不对该类进行解释,因此也不会初始化该类。而Class类的forName方法则是相反,使用forName加载的时候就会将Class进行解释和初始化,forName也有另外一个版本的方法,可以设置是否初始化以及设置ClassLoader,在此就不多讲了。
不知道上面对这两种加载方式的解释是否足够清楚,就在此举个例子吧,例如JDBC DRIVER的加载,我们在加载JDBC驱动的时候都是使用的forName而非是ClassLoader的loadClass方法呢?我们知道,JDBC驱动是通过DriverManager,必须在DriverManager中注册,如果驱动类没有被初始化,则不能注册到DriverManager中,因此必须使用forName而不能用loadClass。
通过ClassLoader我们可以自定义类加载器,定制自己所需要的加载方式,例如从网络加载,从其他格式的文件加载等等都可以,其实ClassLoader还有很多地方没有讲到,例如ClassLoader内部的一些实现等等,本来希望能够讲得简单易懂一点,可是结果自己看回头好像感觉并不怎么样,郁闷,看来自己的文笔还是差太多了,希望能够给一些有需要的朋友一点帮助吧。
发表评论
-
NIO IO区别
2014-01-07 16:12 655转载一篇 我的新书《代码之谜》会有专门的章节 ... -
Spring集成Quartz定时任务框架介绍和Cron表达式详解
2013-01-16 09:25 761在JavaEE系统中,我们会经常用到定时任务,比如每天凌晨生 ... -
转载一篇Java Properties
2012-06-11 18:19 762http://tech.techweb.com.cn/thre ... -
java 线程
2012-05-23 18:07 790http://zhidao.baidu.com/questio ... -
java 并发学习
2012-05-23 11:04 757http://snake1987.iteye.com/blog ... -
java事物总结
2012-05-20 13:19 4297事务处理总结 来源 ... -
MessageDigest使用 完成MD5加密
2012-05-20 13:17 852Java 加密技术:消息摘 ... -
md5加密算法
2012-05-20 13:13 845以下是搜索网上的资料所谓MD5,即"Message- ... -
Class.forName与DriverManager区别
2012-05-17 08:52 6519在学习JDBC的时候,通常有两种方式去注册数据库驱动程序(这里 ...
相关推荐
ClassLoader主要关注的是加载阶段,它根据全限定名(如"java.lang.String")找到对应的.class文件,并将其转换为Class对象。Java提供了三个基础类加载器:Bootstrap ClassLoader、Extension ClassLoader和...
它的主要职责是根据类的全名(包括包名)找到对应的`.class`文件,并将其转换为字节码,然后加载到JVM中。Java的动态加载机制使得我们可以在运行时加载和卸载类,这是Java平台的一大特点。 在`ClassLoader`的层次...
在Java编程语言中,`Classloader`(类加载器)是一个至关重要的组件,它负责将类的`.class`文件从磁盘加载到JVM(Java虚拟机)内存中,使得程序能够执行。这篇博文主要围绕`Classloader`的`loadClass`方法进行深入...
这里我们将详细讨论ClassLoader的运行机制,特别是自定义ClassLoader的设计与实现。 ClassLoader的基本职责是根据类名动态加载对应的类文件。在Java中,类加载过程遵循双亲委派模型(Parent Delegation Model)。这...
创建自定义Classloader需要继承java.lang.ClassLoader类,并重写其关键方法,如`findClass(String name)`或`loadClass(String name)`。这两个方法分别用于查找指定类的字节码和实际加载类。在`findClass`中,我们...
### Java虚拟机中ClassLoader概述与双亲委托机制详解 #### 一、ClassLoader概念与作用 在Java编程语言中,`ClassLoader`是一个非常重要的组件,它负责加载程序运行所需的类文件到Java虚拟机(JVM)中。`ClassLoader`...
3. 如果类还未加载,ClassLoader会在指定的路径(例如类路径,classpath)下寻找对应的.class文件。 4. 找到类文件后,ClassLoader读取文件内容并将其转换为字节码。 5. 接下来,字节码会被验证确保符合Java语言规范...
在这个项目中,我们可以创建一个新的ClassLoader类,覆盖其`loadClass()`方法,实现我们的加载逻辑。我们可能需要读取自定义的位置(比如本地文件系统、网络或数据库)来获取类的字节码,然后使用`defineClass()`...
- 自定义ClassLoader通常需要重写`loadClass()`方法,该方法在找不到类时调用`findClass()`进行实际的加载操作。 - 在`ClassLoaderDemo`这个例子中,可能就展示了如何创建一个自定义的ClassLoader,从非标准位置...
在自定义ClassLoader时,主要需要覆写两个关键方法:`findClass()` 和 `loadClass()`。`loadClass()` 方法通常用于委托父类加载器加载类,如果父类加载器无法加载,再由当前类加载器尝试加载。`findClass()` 方法则...
1. **Bootstrap ClassLoader** 的搜索路径由`System.getProperty("sun.boot.class.path")`指定,通常包含了JVM的核心类库。 2. **Extension ClassLoader** 的搜索路径由`System.getProperty("java.ext.dirs")`指定,...
破解java加密的ClassLoader.java,在classloader植入破解代码
自定义ClassLoader需要继承`java.lang.ClassLoader`类,并重写`findClass()`或`loadClass()`方法。通过这两个方法,你可以控制类的加载来源和方式。 在实际开发中,理解ClassLoader机制可以帮助解决一些问题,例如...
自定义ClassLoader通常需要重写findClass()或loadClass()方法,以控制类的加载行为。 理解ClassLoader的工作原理对于排查类冲突、处理依赖关系以及优化大型J2EE应用的性能具有重要意义。开发者可以通过日志输出、...
ClassLoader是JVM中的一个重要组件,它的主要任务是加载类的二进制数据,转换为Class对象,并供Java应用程序使用。本文将深入浅出地探讨JVM ClassLoader的工作原理和相关知识点。 首先,ClassLoader可以分为三种...
Java ClassLoader机制是Java虚拟机(JVM)中一个至关重要的组成部分,它的主要任务是将类的.class文件加载到JVM中,使得程序能够运行。ClassLoader不仅负责类的加载,还涉及类的验证、初始化等一系列过程。理解...
1. **加载**:加载是ClassLoader工作的起点,它从文件系统或网络中找到类的二进制数据,然后创建一个对应的Class对象。这个过程可以通过自定义ClassLoader来实现,比如从数据库中加载类。 2. **验证**:验证是确保...