Java类加载器( CLassLoader ) 死磕 1、2: 导入 & 分类

JAVA类加载器 死磕系列 目录 by   疯狂创客圈

1.导入
1.1. 从class文件的载入开始
1.2. 什么是类加载器
2. JAVA类加载器分类
2.1. 操作系统的环境变量
2.2. Bootstrap ClassLoader(启动类加载器)
2.3. Extention ClassLoader (扩展类加载器)
2.4 Appclass Loader(应用类加载器)
2.5. 加载器启动次序

2.6. 阅读源码,查看加载器启动顺序
2.7. 本节小结
3. 类的加载过程
3.1. 类的加载分类:隐式加载和显示加载
3.2. 加载一个类的五步工作
3.3. 如何获取类的加载器
3.4 解刨加载器——ClassLoader抽象基类揭秘
3.5. loadClass 关键源代码分析
4 双亲委托与类的加载
4.1. 每个类加载器都有一个parent父加载器
4.2. 类加载器之间的层次关系
4.3. 类的加载次序
4.4 双亲委托机制原理与沙箱机制
4.5. forName方法和loadClass方法的关系

4.6. 使用组合而不用继承
4.7. 各种不同的类加载途径
5. 自定义classLoader
5.1. 自定义类加载器的基本流程
5.2. 入门案例:自定义文件系统类加载器
5.3. 案例的环境配置
5.4 FileClassLoader 案例实现步骤
5.5. FileClassLoader 的类设计
5.6. FileClassLoader 的源码
5.7. FileClassLoader 的使用
5.8. 不同类加载器的命名空间关系
5.9. 自定义加载器的两个要点
6. 基础案例:自定义网络类加载器
6.1. 自定义网络类加载器的类设计
6.2. 文件传输Server端的源码
6.3. 文件传输Client端的源码
6. 4自定义加载器SocketClassLoader的源码
6.5. SocketClassLoader的使用
7. 中级案例:基于加密的自定义网络加载器
7.1. 加密传输Server端的源码
7.2. 加密传输Client端的源码
7.3. 使用亦或实现简单加密和解密算法
7. 网络加密SafeClassLoader的源码
7.5. SafeSocketLoader的使用
8. 高级案例1:基于ASM,和类加载器实现AOP
8.1. ASM字节码操作框架简介
8.2. ASM和访问者模式
8.3. 用于增强字节码的事务类
8.4 通过ASM访问注解
8.5. 通过ASM注入AOP事务代码
8.6. 实现AOP的类加载器
9. 高级案例2:上下文加载器原理和案例
9.1. 父加载器不能访问子加载器的类
9.2. 一个宠物工厂接口
9.3. 一个宠物工厂管理类
9. 4 APPClassLoader不能访问子加载器中的类
9.5. 线程上下文类加载器


【正文】Java类加载器(  CLassLoader ) 死磕 1、2: 

类的加载器导入 & 分类


1 .1.从class文件的载入开始

我们都知道在Java中程序是运行在虚拟机中,我们平常用文本编辑器或者是IDE编写的程序都是.java格式的文件,这是最基础的源码,但这类文件是不能直接运行的。如我们编写一个简单的程序HelloWorld.java,Java虚拟机并不能直接识别我们平常编写的.java源文件,所以需要javac这个命令转换成.class文件,class文件是字节码格式文件。
关键性的一步来了,.class如果加入JVM中呢?
这就需要用到Java的类加载器。

1.2.什么是类加载器

ClassLoader翻译过来就是类加载器,普通的java开发者其实用到的不多,但对于某些框架开发者来说却非常常见。理解ClassLoader的加载机制,也有利于我们编写出更高效的代码。ClassLoader的具体作用就是将class文件加载到jvm虚拟机中去,程序就可以正确运行了。但是,jvm启动的时候,并不会一次性加载所有的class文件,而是根据需要去动态加载。想想也是的,一次性加载那么多jar包那么多class,那内存不崩溃。本文的目的也是学习ClassLoader这种加载机制。

4.2.JAVA类加载器分类

讲解类加载器,首先回顾一下系统的环境变量。

4.2.1.操作系统的环境变量

操作系统中,与java相关的重要的系统环境变量,你知道有哪些吗?
初学java的时候,最害怕是配置环境变量了,关键是不理解,只能战战兢兢地照着书籍上或者是网络上的介绍进行操作。太不照顾新手了,很多菜鸟就是因为卡在环境变量的配置上,遭受了太多的挫败感。 

java相关的环境变量的配置,主要有3个:
JAVA_HOME、PATH、CLASSPATH
具体的配置方式,与操作系统相关。 在Windows下编程时,可以通过系统属性的环境变量界面,配置以上的三个环境变量。
下面介绍一下三个环境变量的含义。

一:JAVA_HOME

指的是你JDK安装的位置,一般默认安装在C盘,如:

C:\Program Files\Java\jdk1.8.0_131

二:PATH

PATH变量,是系统之前就已经一直存在和一直使用的。是系统指令和命令的搜索路径。设置PATH变量的方法是:
PATH=%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;%PATH%;

在原来的PATH路径上添加JDK目录下的bin目录和jre目录的bin。将Java的bin 路径包含在PATH当中后,在命令行窗口就可以直接键入Java命令的名字了,而不再需要键入它的全路径。
比如编译java源文件的javac命令,和执行字节码的java命令。 如果没有将Java的bin路径加入到path中,执行javac命令的方法如下:
C:\Program Files\Java\jdk1.8.0_91\bin\javac  test.java

设置了设置PATH变量之后,执行javac命令的方法如下:

javac  test.java

三:CLASSPATH
CLASSPATH是指向jar包路径和class类路径。
一个配置的例子如下:

CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar

需要注意的是前面的. 点号,代表可以在当前目录搜索java 类文件。

以上的三个Java 系统的环境变量,都与Java的类加载器密切相关。Java语言系统自带有三个类加载器,具体如下图:


4.2.2.Bootstrap ClassLoader(启动类加载器)

Bootstrap ClassLoader是最顶层加载器,主要加载核心类库,%java.home%\lib下的rt.jar、charsets.jar和class等。

注意,这里的 %java.home% 不是JAVA_HOME环境变量,但是与JAVA_HOME环境变量的值相关。有很多文章介绍到这里的时候,将 %java.home% 与JAVA_HOME搞混淆了。
专门澄清一下:%java.home%  指的是JAVA语言运行时的系统属性java.home;JAVA_HOME环境变量,指的是JDK在操作系统中的路径。
通过下面的语句可以获得%java.home% 其值:

    private static void showjavahome()     {         String javahome = System.getProperty("java.home");         Logger.info("javahome = " + javahome);     }

返回的结果是:

javahome = C:\Program Files\Java\jdk1.8.0_131\jre 

对比一下,而前一个小节,我们设置的JAVA_HOME环境变量的值为:

C:\Program Files\Java\jdk1.8.0_131

所以,两者是不一样的。Bootstrap ClassLoader(启动类加载器)加载的类列表清单,保存在系统属性%sun.boot.class.path% 中,通过获取系统属性的值,可以查看。
代码如下:

  private static void showbootpath()     {         String path = System.getProperty("sun.boot.class.path");         Logger.info("sun.boot.class.path = " + path);     }

结果如下:

sun.boot.class.path =  C:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\sunrsasign.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\classes 

通过结果可以看出,系统属性%sun.boot.class.path%中的jar包和类路径,都处于系统属性%java.home% 下。

另外需要注意的是:可以通过启动jvm时指定一个参数选项,来改变Bootstrap ClassLoader的加载目录。这个参数选项为 -Xbootclasspath。
例如:
java -Xbootclasspath:c:/path1/jar1.jar;c:/path2/jar2.jar  Test
被指定的文件追加到默认的bootstrap路径中。

Bootstrap ClassLoader是由C/C++编写的,它本身是虚拟机的一部分,所以它并不是一个JAVA类,也就是无法在java代码中获取它的引用,JVM启动时通过Bootstrap类加载器加载rt.jar等核心jar包中的class文件,比如int.class,String.class都是由它加载。

4.2.3.Extention ClassLoader (扩展类加载器)

Extention ClassLoader (扩展的类加载器),加载系统属性%java.ext.dirs%目录下的jar包和class文件。

Extention ClassLoader (扩展的类加载器)加载的类列表清单,保存在系统属性%java.ext.dirs% 中,通过获取该系统属性的值,可以查看。
代码如下:

  private static void showextpath()     {         String path = System.getProperty("java.ext.dirs");         Logger.info("java.ext.dirs = " + path);     }

返回的结果如下:

java.ext.dirs =
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext;
C:\WINDOWS\Sun\Java\lib\ext

另外需要注意的是:还可以加载-D java.ext.dirs选项指定的目录。


4.2.4.Appclass Loader(应用类加载器)

与前面两个不同,Appclass Loader 是应用程序的类加载器。Bootstrap ClassLoader 和 Extention ClassLoader, 主要是用于虚拟机的系统类加载。
Appclass Loader(应用类加载器)的作用,加载当前应用类路径classpath的所有类。 应用类加载器,也称为SystemAppClass (系统应用类加载器)。Appclass Loader(应用类加载器)加载的类列表清单,保存在系统属性%java.class.path % 中,通过获取该系统属性的值,可以查看。
代码如下:

 private static void showclasspath()     {         String path = System.getProperty("java.class.path");         Logger.info("java.class.path = " + path);     }

返回的结果如下:

java.class.path =  C:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\deploy.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\cldrdata.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\dnsns.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jaccess.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jfxrt.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\localedata.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\nashorn.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunec.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\zipfs.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\javaws.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfxswt.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\management-agent.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\plugin.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;
D:\疯狂创客圈 死磕Java\code\out;
D:\疯狂创客圈 死磕Java\code\lib\cglib-3.2.3.jar;
D:\疯狂创客圈 死磕Java\code\lib\asm-5.0.4.jar;
C:\Program Files\JetBrains\IntelliJ IDEA 2017.1.1\lib\idea_rt.jar 

回顾一下前面设置的操作系统下的环境变量CLASSPATH,如下:

CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar

对比之后,发现有点不对。上面输出的系统属性%java.class.path%的值,与配置的CLASSPATH值不一致,原因是什么呢?
因为我们这里使用了开发工具IntelliJ IDEA,在工程中重新配置了依赖包,相当于将系统属性%java.class.path%的值,重新配置了一下。
具体如下图:

4.2.5.加载器启动次序

前面介绍了的3个类加载器,通过启动JVM启动Java程序的时候,怎样知道具体哪个先行启动呢? 
这里,可以先揭晓答案:
1.  Bootstrap CLassLoader 
2.  Extention ClassLoader 
3.  AppClassLoader

啰嗦一句:BootstrapClassLoader、ExtClassLoader、AppClassLoader通过查阅相应的系统属性sun.boot.class.path、java.ext.dirs和java.class.path来加载资源文件的。


4.2.6.阅读源码,查看加载器启动顺序

三大加载器的启动顺序,可以通过阅读源码来查看。

JVM在启动是,会执行sun.misc.Launcher 类。在该类中,创建一个Extention ClassLoader 扩展类加载器,和一个应用类加载器AppClassLoader。
源码如下:

public class Launcher {     private static Launcher launcher = new Launcher();     private static String bootClassPath =         System.getProperty("sun.boot.class.path");     private ClassLoader loader;     public Launcher() {         // 创建Extention ClassLoader 扩展类加载器         ClassLoader extcl;         try {             extcl = Launcher.ExtClassLoader.getExtClassLoader();         } catch (IOException e) {             throw new InternalError(                 "Could not create extension class loader", e);         }         // 创建应用类加载器AppClassLoader         try {             loader = Launcher.AppClassLoader.getAppClassLoader(extcl);         } catch (IOException e) {             throw new InternalError(                 "Could not create application class loader", e);         }    .....
}
}

在上面的代码中,看到了扩展类加载器和应用类加载器的启动,没有看到Bootstrap Loader的启动。
为什么呢?
从启动次序来说,Bootstrap Loader作为jvm核心的核心,加载位于%java.home% 清单中的JVM 的绝对核心和基础的类,毫无疑问,绝对是首先启动的。由于它是基础加载器,大多数版本jvm的bootstrap loader都不是java实现,或者不是纯java的实现。理论上,Bootstrap ClassLoader 不应该叫他是一个java类。因为,它已经完全不用java实现了。Bootstrap ClassLoader是在jvm启动时,就被构造起来的,负责java平台核心库。
所以,在sun.misc.Launcher 类中,看不到Bootstrap Loader的启动。
完整的三大加载器的启动顺序如下:
1、开始执行java命令后,首先寻找jre目录,寻找jvm.dll,并初始化JVM;
2、产生一个Bootstrap Loader(启动类加载器);
3、创建Extended Loader(标准扩展类加载器);
4、创建AppClass Loader(系统类加载器),并将其父加载器(parent loader)设为Extended Loader。
这里讲到了父加载器,这是一种加载器之间的重要的关系——加载器之间的层次关系。JVM初始化sun.misc.Launcher并创建Extension ClassLoader和AppClassLoader实例。并将ExtClassLoader设置为AppClassLoader的父加载器。
Bootstrap没有父加载器,但是实际上,它却作为所有父加载器为空的加载器的父加载器。呵呵,捋一捋,这句话是不是很绕。

后文中,将对这种层次关系,展开进行详细解读。

4.2.7.本节小结

上面简单介绍了3个ClassLoader。说明了它们加载的路径。并且还提到了-Xbootclasspath和-D java.ext.dirs这两个虚拟机参数选项。 

上面也介绍了三大加载器之间的启动顺序。为后面介绍加载器之间的层次关系,打下了一个铺垫。

本节的代码的类为SysPropertiesShow.java,所在的路径为:
com.crazymakercircle.classLoaderDemo.base 


代码工程:  classLoaderDemo.zip

下载地址:在疯狂创客圈QQ群文件共享。


疯狂创客圈:如果说Java是一个武林,这里的聚集一群武痴, 交流编程体验心得
QQ群链接:
疯狂创客圈QQ群

猜你喜欢

转载自www.cnblogs.com/crazymakercircle/p/9818981.html
今日推荐