1、雙親委派機制
1.1 定義
當一個類加載器收到了類加載的請求的時候,他不會直接去加載指定的類,而是把這個請求委託給自己的父加載器去加載。
如果父類爲空,交給bootstrap classloader 加載。
如果類還是無法被加載到,則觸發findclass,拋出classNotFoundException(findclass這個方法當前只有一個語句,就是拋出classNotFoundException),如果想自己實現類加載器的話,可以繼承classLoader後重寫findclass方法,加載對應的類)
1.2優點
避免類的重複加載
保護程序安全,防止核心API被隨意篡改
2、SPI(service provider interface)定義:
SPI是jdk內置的一種服務提供發現機制。可以用來啓用框架擴展和替換組件,主要是被框架的開發人員使用,比如java.sql.Driver接口,常用的關係型數據有Mysql、Oracle、SQLServer、DB2等,這些不同類型的數據庫使用的驅動程序各不相同,那JDK不可能把所有廠商的驅動都實現,只能制定一個標準接口,其他不同廠商可以針對同一接口做出不同的實現,各個數據庫廠商根據標準來實現自己的驅動,這就是SPI機制。
下面看下Mysql的Driver,以com.mysql.cj.jdbc.Driver 爲例
META-INF目錄的作用是相當於信息包,用來配置應用程序、擴展程序、類加載器和服務manifest.mf文件,在打包時自動生成。
其中manifest.mf裏面描述了程序的基本信息、Main-Class
的入口、jar
依賴路徑Class-Path
而META-INFO/services 的作用是實現接口循環實現類的功能, 除了jdbc模塊的方案(Driver)之外,還有日誌模塊的方案(l4j),xml解析模塊等。
回到正題:
com.mysql.cj.jdbc.Driver的源碼需要往DriverManager進行註冊
最終如下圖所示,需要往 registeredDrivers 裏面進行添加
而registeredDrivers 變量在 DriverManager 靜態初始化的時候會進行調用把真實的類進行加載出來
例子
創建一個 interface 接口
package com.zhianchen.mysqlremark.springboottest.spi; /* * *@Description TODO *@Author chenzhian *@Date 2022/10/21 11:25 */ public interface UserService { void login(); }
實現
package com.zhianchen.mysqlremark.springboottest.spi.Impl; import com.zhianchen.mysqlremark.springboottest.spi.UserService; /* * *@Description TODO *@Author chenzhian *@Date 2022/10/21 11:26 */ public class AppUserLoginServiceImpl implements UserService { @Override public void login() { System.out.println("AppUserLoginServiceImpl"); } }
package com.zhianchen.mysqlremark.springboottest.spi.Impl; import com.zhianchen.mysqlremark.springboottest.spi.UserService; /* * *@Description TODO *@Author chenzhian *@Date 2022/10/21 11:26 */ public class PCUserLoginServiceImpl implements UserService { @Override public void login() { System.out.println("PCUserLoginServiceImpl"); } }
然後在resources 創建目錄 META-INF.services (注意:這裏是services 而不是service!!!!!)
創建文件 com.zhianchen.mysqlremark.springboottest.spi.UserService (這裏的文件名要對應UserService包)
裏面的內容
com.zhianchen.mysqlremark.springboottest.spi.Impl.PCUserLoginServiceImpl
com.zhianchen.mysqlremark.springboottest.spi.Impl.AppUserLoginServiceImpl
創建一個Test
/* * *@Description TODO *@Author chenzhian *@Date 2022/10/21 14:02 */ public class SpiTest { public static void main(String[] args) { ServiceLoader<UserService> serviceLoader= ServiceLoader.load(UserService.class); for (UserService userService:serviceLoader ) { userService.login(); } } }
之前的結果是
PCUserLoginServiceImpl
AppUserLoginServiceImpl
參考
純原創長文-深入理解Java類加載(雙親委派機制及其破壞 JavaSPI和DubboSPI的區別和聯繫)