Java--SPI機(jī)制
SPI全稱為Service Provider Interface口糕,是JDK內(nèi)置的一種服務(wù)提供發(fā)現(xiàn)機(jī)制。簡單來說磕蛇,它就是一種動態(tài)替換發(fā)現(xiàn)機(jī)制景描。例如:有個接口想在運(yùn)行時才發(fā)現(xiàn)具體的實(shí)現(xiàn)類,那么你只需要在程序運(yùn)行前添加一個實(shí)現(xiàn)即可秀撇,并把新加的實(shí)現(xiàn)描述給JDK即可超棺。此外,在程序的運(yùn)行過程中呵燕,也可以隨時對該描述進(jìn)行修改棠绘,完成具體實(shí)現(xiàn)的替換。
Java提供了很多服務(wù)提供者接口(Service Provider Interface,SPI)氧苍,允許第三方為這些接口提供實(shí)現(xiàn)夜矗。常見的SPI有JDBC、JCE让虐、JNDI紊撕、JAXP和JBI等。
這些SPI的接口是由Java核心庫來提供赡突,而SPI的實(shí)現(xiàn)則是作為Java應(yīng)用所依賴的jar包被包含進(jìn)類路徑(CLASSPATH)中对扶。例如:JDBC的實(shí)現(xiàn)mysql就是通過maven被依賴進(jìn)來。
那么問題來了惭缰,SPI的接口是Java核心庫的一部分辩稽,是由引導(dǎo)類加載器(Bootstrap Classloader)來加載的。SPI的實(shí)現(xiàn)類是由系統(tǒng)類加載器(System ClassLoader)來加載的从媚。
引導(dǎo)類加載器在加載時是無法找到SPI的實(shí)現(xiàn)類的,因?yàn)殡p親委派模型中規(guī)定患整,引導(dǎo)類加載器BootstrapClassloader無法委派系統(tǒng)類加載器AppClassLoader來加載拜效。這時候,該如何解決此問題各谚?
線程上下文類加載由此誕生紧憾,它的出現(xiàn)也破壞了類加載器的雙親委派模型,使得程序可以進(jìn)行逆向類加載昌渤。
下面赴穗,我們就用具體的代碼來說明逆向類加載。不過膀息,在距離之前般眉,還是想對spi的使用進(jìn)行一個簡單的說明。
spi使用
首先潜支,通過一張圖來看甸赃,完成spi的實(shí)現(xiàn),需要哪些操作冗酿,需要遵循哪些規(guī)范埠对?
1.代碼編寫
既然是spi,那么就必須先定義好接口裁替。其次项玛,就是定義好接口的實(shí)現(xiàn)類。
2.創(chuàng)建一個文件夾
在項(xiàng)目的\src\main\resources\下創(chuàng)建\META-INF
\services目錄(筆者在網(wǎng)上找了很多文章弱判,很多都沒有告知具體這個文件夾放在哪襟沮,放在其他位置下無法加載得到)
3.文件夾下增加配置文件
在上面META-INF
\services的目錄下再增加一個配置文件,這個文件必須以接口的全限定類名保持一致,例如:com.jiaboyan.test.HelloService
4.配置文件增加描述
上面介紹spi時說道臣嚣,除了代碼上的接口實(shí)現(xiàn)之外净刮,你還需要把該實(shí)現(xiàn)的描述提供給JDK。那么硅则,此步驟就是在配置文件中撰寫接口實(shí)現(xiàn)描述淹父。很簡單,就是在配置文件中寫入具體實(shí)現(xiàn)類的全限定類名怎虫,如有多個便換行寫入暑认。
5.使用JDK來載入
編寫main()方法,輸出測試接口大审。使用JDK提供的ServiceLoader.load()來加載配置文件中的描述信息蘸际,完成類加載操作。
接口定義:
public interface HelloService {
void hello();
}
接口實(shí)現(xiàn):
public class HelloService1Impl implements HelloService {
@Override
public void hello() {
System.out.println("hello jiaboyan");
}
}
public class HelloService2Impl implements HelloService {
@Override
public void hello() {
System.out.println("hello world");
}
}
添加JDK描述徒扶,在META-INF\services目錄下:
com.jiaboyan.test.impl.HelloService1Impl
com.jiaboyan.test.impl.HelloService2Impl
編寫main()方法:
public class Test {
public static void main(String[] agrs) {
ServiceLoader<HelloService> loaders = ServiceLoader.load(HelloService.class);
for (HelloService helloService : loaders) {
helloService.hello();
}
}
}