破坏双亲委派模型
第一次破坏,兼容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。