面试题:什么地方违反了双亲委派模型?
在介绍双亲委派模型之前,我们先要了解一下类加载机制
类加载机制是指将类的class文件读入到内存,并为之创建一个java.lang.Class对象。中间对数据做了校验,转换解析和初始化等操作。
一般情况下我们说了有三种加载器:
最基础:Bootstrap ClassLoader(加载JDK的/lib目录下的类)
次基础:Extension ClassLoader(加载JDK的/lib/ext目录下的类)
普通:Application ClassLoader(程序自己classpath下的类)
双亲委派模型要求如果一个类可以被委派最基础的ClassLoader加载,就不能让高层的ClassLoader加载。
举个例子:
我们实际中用到的一些类如:String,Object等,都是来自于jdk的lib下的rt.jar包,但是如果我们有工作需要在自己的项目中也创建相同名称的类,我们如何来区分呢,双亲委派就完美的解决了这个问题,BootStrap加载rt.jar 包,剩下的由它的子加载器下的子加载器来加载。当然这里的父子不是指的一般意义上的父类和子类。
双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,他首先不会自己去尝试加载这个类,而是把这个请求委派父类加载器去完成。每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个请求(他的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
在很多的时候面试官会问我们如何破坏双亲委派模型和为什么要破坏它
在实际的应用中双亲委派解决了java 基础类统一加载的问题,但是却着实存在着一定的缺席。jdk中的基础类作为用户典型的api被调用,但是也存在被api调用用户的代码的情况,典型的如SPI代码。
所以jdk开发人员就引入了线程上下文类加载器(Thread Context ClassLoader),这类类加载器可以通过java.lang.Thread 类的setContextClassLoader方法进行设置。
其实在jdbc的使用中,我们很好的体会到它的作用,我们平时看到的mysql的加载是这个样子的:
在以上的代码中就实现了注册mysql驱动和获取数据库连接。
以上的代码是DriverManager 的初始化方法loadInitialDrivers
,大家可以从中看到先是获取jdbc.drivers属性,得到类的路径。然后通过系统类加载器加载。
注意driversIterator.next()
最终就是调用Class.forName(DriverName, false, loader)
方法,也就是最开始我们在第一张图中注释掉的那一句代码。
对于自己加载不了的类怎么办,直接用线程上下类加载器加载,通过
ClassLoader cl = Thread.currentThread().getContextClassLoader();
这条语句获取本地线程然后实现上下类加载。牛逼了,所以这个地方Bootstrap Classloader加载器拿到了Application ClassLoader加载器应该加载的类,就打破了双亲委派模型