一.局限性

父加载器无法向下识别子加载器加载的资源(假定 A 作为 B 的 parent,A 加载的类 对 B 是可见的;然而 B 加载的类 对 A 却是不可见的)这是由 classloader 加载模型中的可见性(visibility)决定的。

二.实际体现

  1. 最典型不适用的场景便是 SPI 的使用。Java 在核心类库中定义了许多接口,并且还给出了针对这些接口的调用逻辑,然而并未给出实现。开发者要做的就是定制一个实现类,在 META-INF/services 中注册实现类信息,以供核心类库使用。java.sql.Driver 是最为典型的 SPI 接口,java.sql.DriverManager 通过扫包的方式拿到指定的实现类,完成 DriverManager的初始化。<br />根据双亲委派的可见性原则,**启动类加载器** 加载的 DriverManager 是不可能拿到 **系统应用类加载器** 加载的实现类 ,这似乎通过某种机制打破了双亲委派模型。<br />为解决上述问题,引入线程上下文类加载器,可以通过ThreadsetContextClassLoader设置

SPI 是如何打破双亲委派模型的呢?

java.sql.DriverManager#loadInitialDrivers

  1. ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
  2. Iterator<Driver> driversIterator = loadedDrivers.iterator();
  3. /* Load these drivers, so that they can be instantiated.
  4. * It may be the case that the driver class may not be there
  5. * i.e. there may be a packaged driver with the service class
  6. * as implementation of java.sql.Driver but the actual class
  7. * may be missing. In that case a java.util.ServiceConfigurationError
  8. * will be thrown at runtime by the VM trying to locate
  9. * and load the service.
  10. *
  11. * Adding a try catch block to catch those runtime errors
  12. * if driver not available in classpath but it's
  13. * packaged as service and that service is there in classpath.
  14. */
  15. try{
  16. while(driversIterator.hasNext()) {
  17. driversIterator.next();
  18. }
  19. } catch(Throwable t) {
  20. // Do nothing
  21. }
  22. return null;
  1. public static <S> ServiceLoader<S> load(Class<S> service) {
  2. ClassLoader cl = Thread.currentThread().getContextClassLoader();
  3. return ServiceLoader.load(service, cl);
  4. }

通过从线程上下文(ThreadContext)获取 classloader,借助这个classloader可以拿到实现类的Class。(源码上讲,这里是通过 Class.forName 配合 classloader拿到的)线程上下文 classloader并非具体的某个loader,一般情况下是application classloader,但也可以通过 java.lang.Thread#setContextClassLoader这个方法指classloader。

综上,为什么说 Java SPI 的设计会违反双亲委派原则呢?
首先双亲委派原则本身并非 JVM 强制模型。
SPI 的调用方和接口定义方很可能都在 Java 的核心类库之中,而实现类交由开发者实现,然而实现类并不会被启动类加载器所加载,基于双亲委派的可见性原则,SPI 调用方无法拿到实现类。
SPI Serviceloader 通过线程上下文获取能够加载实现类的classloader,一般情况下是 application classloader,绕过了这层限制,逻辑上打破了双亲委派原则。