双亲委派
-
类加载器
-
双亲委派模型
-
破坏双亲委派模型
- Tomcat
- JDBC
-
模块化技术与类加载机制
类加载器
Class 文件是通过类加载器
加载到虚拟机中的,这个动作在虚拟机外部实现,以便让应用程序自己决定如何去获取所需的类,实现这个动作的代码被称为“类加载器”。
对于任意一个类,必须由加载它的类加载器
和这个类本身
一起共同确立其在java虚拟机中的唯一性。每一个类加载器都拥有一个独立的类名称空间。
一个Tomcat可以运行多个war包,说明一个Tomcat 可以运行多个Web应用,万一这些应用中有类的全限定名一样,但是具体实现不一样,这样的话Tomcat 如何保证不冲突?
Tomcat 给每个Web应用创建一个类加载器实例(WebAppClassLoader), 该加载器重写了 loadClass 方法,优先加载当前应用目录下的类,如果找不到,再往上找。这样的话就做到了Web 应用的隔离。Tomcat破坏双亲委派下面再详细介绍。
什么情况下需要自定义类加载器
1️⃣ 隔离加载类。在某些框架内进行中间件与应用的模块隔离,把类加载器到不同的环境。比如,阿里内某容器框架通过自定义类加载器确保应用中依赖的 jar
包不会影响到中间件运行时使用的 jar
包。
2️⃣ 修改类加载方式。类的加载模型并非强制,除了 Bootstrap 外,其他的加载并非一定要引入,或者根据实际情况在某个时间点进行按需的动态加载。
3️⃣ 扩展加载源。比如从数据库、网络,甚至是电视机顶盒进行加载。
4️⃣ 防止源码泄露。Java 代码容易被编译和篡改,可以进行编译加密。那么类加载器也需要自定义,还原加密的字节码。
双亲委派模型
上图每个加载器都有对应的加载搜索范围
- Bootstrap ClassLoader:由底层的c++实现,负责在虚拟机启动时加载Jdk核心类库(如:rt.jar、resources.jar、charsets.jar等)以及加载后两个类加载器。这个ClassLoader完全是JVM自己控制的,需要加载哪个类,怎么加载都是由JVM自己控制,别人也访问不到这个类。
- Extension ClassLoader:是一个普通的Java类,继承自ClassLoader类,负责加载{JAVA_HOME}/jre/lib/ext/目录下的所有jar包。
- AppClassLoader:负责加载应用程序classpath目录下的所有jar和class文件。
所谓双亲委派
是指每次收到类加载请求时,先将请求委派给父类加载器完成(所有加载请求最终会委派到顶层的Bootstrap ClassLoader加载器中),如果父类加载器无法完成这个加载(该加载器的搜索范围中没有找到对应的类),子类尝试自己加载, 如果都没加载到,则会抛出 ClassNotFoundException 异常。(类加载器加载完类会放入自己的缓存中,类加载器在加载类时先应该看自己的缓存中有没有,没有的话再进行双亲委派)。
为什么需要双亲委派?好处是什么?
这种机制使得类加载器之间有严格的层次关系,也使得java类有了层次关系。
可以避免重复加载
,当父类加载器已经加载了该类的时候,就没有必要 ClassLoader 再加载一次。
安全性
:有效的防止java 核心 api 被篡改。如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义的类型。可以避免我们自己写的类和 JDK 自带的核心类发生冲突。
双亲委派是如何实现的?
双亲委派模型对于保证Java程序的稳定运作很重要,但它的实现并不复杂。
实现双亲委派的代码都集中在java.lang.ClassLoader的loadClass()方法之中
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先检查是否已经加载过了
Class<?> c = fin