历史上有哪几次双亲委派机制的破坏?

双亲委派机制经历了三次破坏:

第一次破坏

双亲委派模型的第一次破坏发生在双亲委派模型出现之前,在 JDK 1.2 之前的早期阶段。

由于双亲委派模型是在 JDK 1.2 中引入的,而类加载器的概念和抽象类java.lang.ClassLoader已经存在于 Java 的初始版本中。为了向下兼容旧代码,无法通过技术手段防止loadClass()方法被子类覆盖。因此,在 JDK 1.2 之后的java.lang.ClassLoader中添加了一个新的受保护方法findClass(),鼓励开发者在重写类加载逻辑时尽可能使用这个方法,而不是在loadClass()方法中编写代码。

第二次破坏

双亲委派模型的第二次破坏是由于模型本身的限制所导致的,当基础类型需要调用用户代码时会面临问题。

一个典型的例子是JDBC(Java Database Connectivity):

不同厂商有不同的JDBC实现,Java在核心库的\lib目录下定义了相应的SPI(Service Provider Interface),因此这部分由启动类加载器加载。然而,各个厂商的实现无法放在核心库中,只能放在应用程序的classpath中,只能由应用类加载器加载。这样一来,启动类加载器无法加载厂商提供的SPI服务代码。

为了解决这个问题,引入了一种不太优雅的设计:线程上下文类加载器(Thread Context ClassLoader)。通过java.lang.Thread类的setContextClassLoader()方法可以设置线程上下文类加载器。如果在线程创建时没有进行设置,它将从父线程中继承一个。如果在应用程序的全局范围内没有进行设置,默认的类加载器就是应用程序类加载器。

JNDI(Java Naming and Directory Interface)服务使用线程上下文类加载器来加载所需的SPI服务代码,这是一种父类加载器向子类加载器发起类加载请求的行为。

第三次破坏

双亲委派模型的第三次破坏是由于用户对程序动态性的追求而导致的,例如代码热替换(Hot Swap)和模块热部署(Hot Deployment)。

OSGi(Open Service Gateway Initiative)实现了模块化热部署的关键在于其自定义的类加载器机制。在OSGi中,每个程序模块(在OSGi中称为Bundle)都有自己的类加载器。当需要更换一个Bundle时,会替换掉Bundle以及与之关联的类加载器,实现代码的热替换。在OSGi环境下,类加载器不再遵循双亲委派模型的树状结构,而是进一步发展为更加复杂的网状结构。

这些破坏双亲委派模型的情况使得类加载机制更加灵活和动态化,但同时也增加了复杂性和潜在的问题。

标签: java, Java面试题, Java问题合集, Java编程, Java问题精选, va常见问题