Java双亲委派和类加载器
- Java类生命周期
- 主要内容
- 类加载器的分类
- Bootstrap ClassLoader
- 非Bootstrap ClassLoader
- Extension ClassLoader
- Application ClassLoader
- User ClassLoader
- 类加载的命名空间
- 问题提出
- 双亲委派机制
- 问题解答
- 破坏双亲委派
- 破坏双亲委派-第一次
- 破坏双亲委派-第二次
- 思考题
Java类生命周期
Java类加载分为以下几个步骤:
只有加载步骤中的读取二进制流与初始化部分,能够被上层开发者,也就是大部分的Java程序员控制,而剩下的所有步骤,都是由JVM掌控,其中细节由JVM的开发人员处理,对上层开发者来说是个黑盒。
面向对象SOLID: 单一功能、开闭、里氏替换、接口隔离、依赖反转。
主要内容
类加载器的分类
类加载器的分类属于JVM规范,属于一种抽象的概念。各个不同的JVM实现方式是不一定一样的。
JVM规范中类加载器分为两大类,分为启动类加载器和非启动类加载器。
本文主要关注HotSpot虚拟机。
Bootstrap ClassLoader
Bootstrap ClassLoader是嵌套在JVM内部的,它主要用于加载java的核心类库。比如<JAVA_HOME>/lib
下的jar包。或者是由启动参数来指定路径下的核心类库。
此外,为了安全性,Bootstrap ClassLoader只加载了包名在白名单中的文件。
非Bootstrap ClassLoader
Extension ClassLoader
是由内部类ext classloader来实现的。主要加载<JAVA_HOME>/lib/ext
目录下或者是由系统变量指定的路径中的类库。
Extentsion ClassLoader希望加载的是Java API的拓展,是对Java类库的一些补充能力。
Application ClassLoader
是由内部类app classloader来实现的。主要加载classpath/java.class.path
目录下或者是系统属性指定的路径中的类库。
Application ClassLoader希望加载的是上层程序员编写的一些代码以及一些第三方的类库。可以说程序员平时编写的代码都是由Application ClassLoader来加载的。
User ClassLoader
可以让用户获取任何地方的字节码,并对它们进行加载。这就印证了在Java类加载机制中,允许用户从各个渠道获取class文件的二进制流来进行加载的结论。
用户自定义的类加载器只需要继承java.lang.ClassLoader, 然后单独实现获取二进制流的逻辑。而后续的步骤必须让java.lang.ClassLoader中内置的逻辑来处理。用户无权进行重写和干涉。
类加载的命名空间
问题提出
1.不同的类加载器,除了读取二进制流的动作和范围不一样,后续的加载逻辑是否也不一样?
2. 遇到限定名一样的类,这么多类加载器会不会产生混乱?
JVM规范:每个类加载器都有属于自己的命名空间。
双亲委派机制
在被动的情况下,当一个类加载器收到加载请求,他不会首先自己去加载,而是传递给自己的父亲加载器。
这样所有的类都会首先传递到最上层的Bootstrap ClassLoader,只有父亲加载器无法完成加载,那么儿子加载器才会自己去尝试加载。
无法加载:根据类的限定名,类的加载器没有在自己负责的加载路径中找到该类。
问题解答
-
不同的类加载器,除了读取二进制流的动作和范围不一样,后续的加载逻辑是否也不一样?
我们认为除了Bootstrap ClassLoader,所有的非Bootstrap ClassLoader都继承了
java.lang.ClassLoader,都由这个类的defineClass进行后续处理。 -
遇到限定名一样的类,这么多类加载器会不会产生混乱?
越核心的类库被越上层的类加载器加载,而某限定名的类一旦被加载过了,被动情况下,就
不会再加载相同限定名的类。这样,就能够有效避免混乱。
破坏双亲委派
但是双亲委派模型并不是一个具有强约束力的模型,因为他存在设计权限。在大部分被动情况下,他是生效并且好用的。但在一些情况可以被主动破坏。
破坏双亲委派-第一次
破坏双亲委派-第二次
上层的BootstrapClassLoader对下层的Application ClassLoader进行了委派加载。
思考题
能不能自己写一个限定名为java.lang.Stringl的类,并在程序中调用它?
如果在项目中单纯定义一个java.lang.String类,那么根据双亲委派原则会加载源码中的String,自定义类无法加载报错。如果使用自定义加载器去加载自定义的String类也不行,JDK源码好像限制了"java"开头的类,也就是保护核心源码的安全机制,防止乱改核心源码。
参考资料:【JVM】Java双亲委派、类加载器这块算是玩明白了