前言:打破双亲委派有几种方式,先了解一下什么是双亲委派,好端端为什么要打破双亲委派

双亲委派:如果一个类加载器收到加载某个类的加载请求,则该类加载器不会去加载该类,而是把这个请求给父类加载器,每个一个层次的加载器都是如此,因此所有的类加载请求最终都会传到顶端的启动类加载器,只有到父类加载器在其范围找不到该类时,会将结果返回给子类加载器,最终会找到可以加载该类的子类加载器。

双亲委派的作用:保证JDK的核心类优先加载
双亲委派模型要求如果一个类可以委派给最基础的classloader加载,就不能让高层的classloader进行加载。

双亲委派流程:
子类先委托父类加载;父类加载器有自己的范围,在自有范围中未找到则不加载,返回给子类加载器;子类加载器收到父类加载器无法加载的消息,则自己加载

顺便介绍一下三种类加载器:
1.启动类加载器:C++实现,在java里无法获取,负责加载JAVA_HOME/lib 下的类
2.拓展类加载器:JAVA实现,在java里获取,负责加载JAVA_HOME/lib/ext 下的类
3.系统类加载器/应用程序加载器:简单来说,我们写的代码默认就是由他来进行加载(程序自己classpath下的类)ClassLoader.gerSystemClassloader

为什么要打破?
举一个最常见的栗子:我们常用数据库驱动Driver接口,Driver定义在jdk当中,当其实现却是各个数据库服务商,例如,mysql的MYSQL CONNECROR,所有这就有个问题,DriverManger要加载各个Driver接口实现类,然后进行管理,但是DriverManager是由启动类加载器进行加载的,而这个启动类加载器默认值加载JAVA_HOME下面的lib,但我们真正要加载的是各个实现类,需要有系统类加载器进行加载,这个时候就需要启动类加载器委托系统类加载器去加载Driver实现类,从而破坏了双亲委派。

打破方式:
1.自定义类加载,重写loadclass方法
因为双亲委派的机制都是通过这个方法实现的,这个方法可以指定类通过什么类加载器来进行加载,所有如果改写他的加载规则,相当于打破双亲委派机制

2.使用线程上下文类
双亲委派模型的第二次“破坏”是由这个模型自身的缺陷所导致的,双亲委派很好的解决了各个类加载器的基础类统一问题,基础类之所以“基础”,是因为他们总被用户代码所调用,但是如果基础类又要重新调用用户代码,那咋办?
比如说JNDI是java的标准服务,它的代码是由启动类加载器进行加载的,但是jndi的作用就是进行资源的集中管理和查找,它需要调用由开发人员开发在classpath下的类代码,但是启动类加载器不会进行加载。
所以引入线程上下类加载器,通过java.lang.Thread类的setContextClassLoader()方法进行设置。如果创建线程是还未设置,它会从父线程继承一个,如果在应用程序全局范围内没有设置,那么这个线程上下类加载器就是应用程序类加载器。
那么这样JNDI服务使用这个线程上下类加载器去加载所需的spi代码,也就是父类加载器请求子类加载器去完成类加载的动作,这个实际是打通了双亲委派的逆向层次结构。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐