破坏双亲委派模型
第一次破坏,兼容JDK1.2
之前的代码
双亲委派模型是在JDK1.2
出现的,为了兼容已有代码,但类加载器的概念和抽象类
java.lang.ClassLoader
则在Java
的第一个版本就存在,面对已经存在用户自定义
类加载器的代码,无法以技术手段避免loadClass()
被子类覆盖的可能性,只能在
在java.lang.ClassLoader
中添加一个新的protected
方法findClass()
,并引导
用户编写的类加载逻辑时尽可能重写这个方法,而不是在loadClass()
中编写代码。
第二次破坏,自身缺陷导致的
双亲委派很好地解决了各个类加载器协作是基础类型的一致性问题(越基础的类由越上层
的加载器进行加载),基础类型之所以被称为“基础”,是因为它们总是作为被继承、调用的
API
存在,但如果有基础类型又要调用回用户的代码情况呢?
典型的例子便是JNDI
服务,JNDI
存在的目的就是对资源进行查找和集中管理,
需要调用其他厂商实现并部署在应用程序的ClassPath
下的JNDI
服务提供者
接口(Service Provider Interface
,SPI
)的代码,问题是启动类加载器绝不可能认识、加载这些代码。
为了解决这个困境,Java
的设计团队引入了一个不大优雅的设计:线程上下文加载器(Thread Context ClassLoader
)。
这个类加载器可以通过java.lang.Thread
类的setContextClassLoader()
方法进行设置,如果创建线程时未设置,
将会从父线程中继承一个,默认是应用程序类加载器。
JNDI
服务使用这个线程上下文加载器去加载所需要的SPI
服务代码,这是一种父类加载器去请求子类加载器完成类加载
的行为。已经违背了双亲委派模型的一般性原则,但也是无可奈何的事。Java
中涉及SPI
的加载基本上都采用这种方式
完成,例如JNDI
、JDBC
、JCE
、JAXB
、和JBI
等。
当SPI
的服务提供者多于一个的时候,代码就只能根据具体提供者的类型来硬编码判断,
为了消除这种极不优雅的实现方式,在JDK6
时,JDK
提供了java.util.ServiceLoader
类,
以META-INF/services
中的配置信息,辅以责任链模式,才算给SPI
的加载提供了一种相对合理的解决方案。
第三次破坏,追求程序动态性
Java
模块化,需要到JDK9
。