双亲委派机制大家都知道吧,其实它也是有缺陷的,这次我们要聊的就是双亲委派机制的局限性,一起来了解一下吧。
一、因为双亲委派模型是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常见问答相关知识的话,就请一直关注我们的网站吧。
推荐阅读: