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