双亲委派机制可以被打破吗?模型可以被破坏吗?

TheDisguiser 2020-05-10 16:45:01 java常见问答 8338

大家都知道双亲委派机制是什么吧,那你们知道双亲委派机制该怎么打破吗?它的模型又是怎么被破坏的呢?下面小编就带你详细了解了解吧。

一、双亲委派机制是什么?

一般的说,一个个特定的类加载器它在接到需要加载类的请求时,它会首先查看自己已加载完的类中是否包含这个类,如果有就返回,没有的话就会把加载的任务交给父类加载器加载,以此递归,父类加载器如果可以完成类加载任务,就返回它,当父类加载器无法完成这个加载任务时,才会不得已自己去加载。

双亲委派机制可以被打破吗?模型可以被破坏吗?

ClassLoaderA和ClassLoaderB是我们已经实现的类加载器,这里把它们都指定了父加载器为APPClassLoader,由于往上最多只能拿到SystemClassLoader的引用,所以父加载器最多只能指定到SystemClassLoader。

二、双亲委派机制该怎么打破?

两种方法:

1. 自定义类加载器,重写loadClass方法;

2. 使用线程上下文类加载器;

三、双亲委派机制模型怎么被破坏的?

双亲委派机制有三次破坏历史:

一次破坏

因为双亲委派模型是JDK1.2之后才被引入,而类加载器和抽象类java.lang.ClassLoader则在JDK1.0时就已经存在,所以在面对已存在的用户自定义类加载器的实现代码时,Java设计者引入双亲委派模型时不得不做出一些妥协。在此之前,用户去继承java.lang.ClassLoader的唯一目的就是为了重写loadClass()方法,这是源于虚拟机进行类加载的时候会调用加载器的私有方法loadClassInternal(),而这个方法的唯一逻辑就是去调用自己的loadClass()。

二次破坏

第二次“破坏”是因为模型自身缺陷所致,双亲委派很好地解决了各个类加载器的基础类的统一问题,那问题就来了,如果基础类又要调用回用户的代码,该怎么办?

例:JNDI服务

它的代码是启动类加载器去加载的,JNDI的目的是为了对资源进行集中管理与查找,它需要调用由独立厂商实现并部署在应用程序的ClassPath下的JNDI接口提供者的代码,但启动类加载器显然不会知道这些代码。

由此,Java的设计团队引入了一个不怎么优雅的设计:线程上下文类加载器;这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置,在创建线程时如果还未设置的话,他会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那么这个类加载器默认就是应用程序类加载器。

这样,JNDI服务就可以去加载它所需要的SPI代码,但这种打通了双亲委派模型层次结构由此来逆向使用类加载器的行为,实际上就已经违背了双亲委派模型的一般性原则!

三次破坏

双亲委派模型的第三次“被破坏”源于用户对程序动态性的追求。

OSGi实现模块化热部署的核心在于它自定义的类加载器机制的实现。所有的程序模块(Bundle)都有一个自己的类加载器,当需要更换一个Bundle时,就把Bundle连同类加载器一起换掉来实现代码的热替换。在OSGi幻境下,类加载器不再是双亲委派模型中的树状结构,而是进一步发展为更加复杂的网状结构,当受到类加载请求时,OSGi将按照下面的顺序进行类搜索:

1)把java.*开头的类委派给父类加载器加载。

2)不然,把委派列表名单内的类委派给父类加载器加载。

3)不然,把Import列表中的类委派给Export这个类的Bundle的类加载器加载。

4不然,就查找当前Bundle的ClassPath,并使用自己的类加载器加载。

5)不然,就查找类是否在自己的Fragment Bundle中,如果在,则委派给Fragment Bundle的类加载器加载。

6)不然,就查找Dynamic Import列表的Bundle,委派给对应Bundle的类加载器加载。

7)不然,类加载器失败。

好了,这就是本文的全部内容了,如果你还想要了解更多java常见问答相关知识的话,请多多关注我们的更新吧。

推荐阅读:

双亲委派机制的局限有哪些?  

双亲委派机制的三个特性是什么?有什么作用 

双亲委派机制及使用原因是什么?