爲什麼說SPI打破雙親委派機制
簡單介紹ClassLoader的雙親委派機制:
java類通過Classloader加載,Classloader之間有繼承關係,AppClassLoader繼承ExtClassloader繼承BootstrapClassloader。在類加載時,子加載器會調用父加載器來加載類,如果父加載器不能加載類,纔會交給子加載器來加載;如果子加載器也加載失敗,那麼就報異常。
可以看出雙親委派機制是一種至下而上的加載方式,那麼SPI是如何打破這種關係?
以JDBC加載驅動爲例:
在JDBC4.0之後支持SPI方式加載java.sql.Driver的實現類。SPI實現方式爲,通過ServiceLoader.load(Driver.class)方法,去各自實現Driver接口的lib的META-INF/services/java.sql.Driver文件裏找到實現類的名字,通過Thread.currentThread().getContextClassLoader()類加載器加載實現類並返回實例。
驅動加載的過程大致如上,那麼是在什麼地方打破了雙親委派模型呢?
先看下如果不用Thread.currentThread().getContextClassLoader()加載器加載,整個流程會怎麼樣。
- 從META-INF/services/java.sql.Driver文件得到實現類名字DriverA
- Class.forName(“xx.xx.DriverA”)來加載實現類
- Class.forName()方法默認使用當前類的ClassLoader,JDBC是在DriverManager類裏調用Driver的,當前類也就是DriverManager,它的加載器是BootstrapClassLoader。
- 用BootstrapClassLoader去加載非rt.jar包裏的類xx.xx.DriverA,就會找不到
- 要加載xx.xx.DriverA需要用到AppClassLoader或其他自定義ClassLoader
- 最終矛盾出現在,要在BootstrapClassLoader加載的類裏,調用AppClassLoader去加載實現類
這樣就出現了一個問題:如何在父加載器加載的類中,去調用子加載器去加載類?
- jdk提供了兩種方式,Thread.currentThread().getContextClassLoader()和ClassLoader.getSystemClassLoader()一般都指向AppClassLoader,他們能加載classpath中的類
- SPI則用Thread.currentThread().getContextClassLoader()來加載實現類,實現在覈心包裏的基礎類調用用戶代碼