为啥说java spi破坏双亲委派模型( 四 )

java.sql.Driver 是最为典型的 SPI 接口,java.sql.DriverManager 通过扫包的方式拿到指定的实现类,完成 DriverManager的初始化。
等等,似乎有什么不对,根据双亲委派的可见性原则,启动类加载器 加载的 DriverManager 是不可能拿到 系统应用类加载器 加载的实现类 ,这似乎通过某种机制打破了双亲委派模型。
双亲委派模型并非强制模型SPI 是如何打破双亲委派模型的呢?
java.sql.DriverManager#loadInitialDriversServiceLoader\u0026lt;Driver\u0026gt; loadedDrivers = ServiceLoader.load(Driver.class);Iterator\u0026lt;Driver\u0026gt; driversIterator = loadedDrivers.iterator();?/* Load these drivers, so that they can be instantiated. * It may be the case that the driver class may not be there * i.e. there may be a packaged driver with the service class * as implementation of java.sql.Driver but the actual class * may be missing. In that case a java.util.ServiceConfigurationError * will be thrown at runtime by the VM trying to locate * and load the service. * * Adding a try catch block to catch those runtime errors * if driver not available in classpath but it\u0026#39;s * packaged as service and that service is there in classpath. */try{ while(driversIterator.hasNext()) { driversIterator.next(); }} catch(Throwable t) {// Do nothing}return null;java.util.ServiceLoader#load(java.lang.Class\u0026lt;S\u0026gt;)public static \u0026lt;S\u0026gt; ServiceLoader\u0026lt;S\u0026gt; load(Class\u0026lt;S\u0026gt; service) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl);}通过从线程上下文(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,绕过了这层限制,逻辑上打破了双亲委派原则。
参考资料
Understanding Extension Class Loading
Class Loaders in Java | BaeldungChapter 5. Loading, Linking, and Initializing
https://javarevisited.blogspot.com/2012/12/how-classloader-works-in-java.html
个人公众号
ZeaTalk
■网友
使用Class.forName()不就是相当于在使用的时候把类名写死了么,SPI就是要解决这个问题,我提供一个接口,你需要自己的实现类直接打个JAR包发到classpath就可以了。
■网友
看ServiceLoader的源码就知道了: private S nextService() { if (!hasNextService()) throw new NoSuchElementException(); String cn = nextName; nextName = null; Class\u0026lt;?\u0026gt; c = null; try { //这个loader如果不指定是默认用的AppClassLoader c = Class.forName(cn, false, loader); } catch (ClassNotFoundException x) { fail(service, "Provider " + cn + " not found"); } if (!service.isAssignableFrom(c)) { fail(service, "Provider " + cn + " not a subtype"); } try { S p = service.cast(c.newInstance()); providers.put(cn, p); return p; } catch (Throwable x) { fail(service, "Provider " + cn + " could not be instantiated", x); } throw new Error(); // This cannot happen }
推荐阅读
- 为啥看到书柜上的藏书会有心旷神怡的感觉
- 为啥知乎上普便有一种【我在北上广深打工,所以拥有更好的视野】这样的错觉
- 为啥工商银行的用户体验如此之差
- 汽车|看了中消协4S店服务测评调查结果,终于知道法系车为啥卖不好了
- 你为啥从窝窝商城离职?
- 为啥5G和2.4G默认的BSSID是相同的
- 为啥电器实体店的价格比淘宝贵那么多
- 现在在线学习视频有很多了,为啥大部分人还是喜欢下载下来观看
- dart这编程语言现在发展怎么样了,语法与Java,c#很相似,甚至更简洁
- 为啥到现在你还没有女朋友 ?
