java開(kāi)發(fā)ServiceLoader實(shí)現(xiàn)機(jī)制及SPI應(yīng)用_第1頁(yè)
java開(kāi)發(fā)ServiceLoader實(shí)現(xiàn)機(jī)制及SPI應(yīng)用_第2頁(yè)
java開(kāi)發(fā)ServiceLoader實(shí)現(xiàn)機(jī)制及SPI應(yīng)用_第3頁(yè)
java開(kāi)發(fā)ServiceLoader實(shí)現(xiàn)機(jī)制及SPI應(yīng)用_第4頁(yè)
java開(kāi)發(fā)ServiceLoader實(shí)現(xiàn)機(jī)制及SPI應(yīng)用_第5頁(yè)
已閱讀5頁(yè),還剩2頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第java開(kāi)發(fā)ServiceLoader實(shí)現(xiàn)機(jī)制及SPI應(yīng)用目錄前言如何繞過(guò)雙親委派模式ServiceLoader實(shí)現(xiàn)機(jī)制SPI在各個(gè)框架上的應(yīng)用小結(jié)

前言

做過(guò)javaweb開(kāi)發(fā)的小伙伴大多數(shù)時(shí)候都需要鏈接數(shù)據(jù)庫(kù),這個(gè)時(shí)候就需要配置數(shù)據(jù)庫(kù)引擎DriverClassName參數(shù),這樣我們的java應(yīng)用才能通過(guò)數(shù)據(jù)庫(kù)廠商給的Driver與指定的數(shù)據(jù)庫(kù)建立通信。但是這里就有一個(gè)疑問(wèn):

java.sql.Driver是jdk自帶的接口,它是由BoostrapClassLoader加載的,DriverClassName是外部廠商提供的具體實(shí)現(xiàn),是由AppClassLoader加載的,要建立與數(shù)據(jù)庫(kù)的通信,必然是通過(guò)java.sql.Driver接口方法發(fā)起的,那么在java.sql.Driver是如何拿到具體實(shí)現(xiàn)的呢?它是不是違背了ClassLoader的雙親委派模式呢?

如何繞過(guò)雙親委派模式

為了拿到AppClassLoader中加載的java.sql.Driver實(shí)現(xiàn)類,我們可以查看一下DriverManager是怎么處理的:

publicstaticDrivergetDriver(Stringurl)

throwsSQLException{

println("DriverManager.getDriver(""+url+"")");

ensureDriversInitialized();

......

privatestaticvoidensureDriversInitialized(){

......

AccessController.doPrivileged(newPrivilegedActionVoid(){

publicVoidrun(){

//核心代碼ServiceLoader

ServiceLoaderDriverloadedDrivers=ServiceLoader.load(Driver.class);

IteratorDriverdriversIterator=loadedDrivers.iterator();

try{

while(driversIterator.hasNext()){

driversIterator.next();

}catch(Throwablet){

//Donothing

returnnull;

......

我們最終可以發(fā)現(xiàn),DriverManager通過(guò)ServiceLoader.load(Driver.class)就拿到了我們配置的DriverClassName實(shí)現(xiàn)類。這就實(shí)現(xiàn)在DriverManager中拿到了外部提供的Driver實(shí)現(xiàn),繞過(guò)來(lái)雙親委派模式。

ServiceLoader實(shí)現(xiàn)機(jī)制

我們來(lái)看一下ServiceLoader是如何實(shí)現(xiàn)SPI機(jī)制的,先從ServiceLoader.load()方法入手:

publicstaticSServiceLoaderSload(ClassSservice){

//從當(dāng)前線程中獲取ClassLoader

ClassLoadercl=Thread.currentThread().getContextClassLoader();

//創(chuàng)建一個(gè)ServiceLoader對(duì)象

returnnewServiceLoader(Reflection.getCallerClass(),service,cl);

1.從當(dāng)前線程中獲取ClassLoader;因?yàn)樵趧?chuàng)建AppClassLoader后,將AppClassLoader設(shè)置進(jìn)當(dāng)前線程的上下文中;

2.根據(jù)ClassLoader以及目標(biāo)接口類創(chuàng)建一個(gè)ServiceLoader對(duì)象;

其實(shí)ServiceLoader核心代碼在hasNext()方法中:

@Override

publicbooleanhasNext(){

if(acc==null){

returnhasNextService();

}else{

PrivilegedActionBooleanaction=newPrivilegedAction(){

publicBooleanrun(){returnhasNextService();}

returnAccessController.doPrivileged(action,acc);

最終都會(huì)調(diào)用到hasNextService()方法中:

privatebooleanhasNextService(){

//nextProvider默認(rèn)為null,如果通過(guò)next()取出來(lái)了,nextProvider就會(huì)變成null

while(nextProvider==nullnextError==null){

try{

//找到目標(biāo)實(shí)現(xiàn)類

Classclazz=nextProviderClass();

if(clazz==null)

returnfalse;

if(clazz.getModule().isNamed()){

//ignoreclassifinnamedmodule

continue;

//判斷service接口是否和clazz有父子關(guān)系

if(service.isAssignableFrom(clazz)){

ClassextendsStype=(ClassextendsS)clazz;

//獲取無(wú)參構(gòu)造函數(shù)

ConstructorextendsSctor

=(ConstructorextendsS)getConstructor(clazz);

//包裝成一個(gè)ProviderImpl對(duì)象

ProviderImplSp=newProviderImplS(service,type,ctor,acc);

//并賦值給nextProvider

nextProvider=(ProviderImplT

}else{

fail(service,clazz.getName()+"notasubtype");

}catch(ServiceConfigurationErrore){

nextError=e;

returntrue;

外部提供的實(shí)現(xiàn)類一定要有一個(gè)無(wú)參構(gòu)造函數(shù),否則會(huì)導(dǎo)致ServiceLoader加載失?。?/p>

我們下面再繼續(xù)深入看看ServiceLoader是怎么找到實(shí)現(xiàn)類的:

privateClassnextProviderClass(){

if(configs==null){

try{

//拼接文件名:"META-INF/services/接口名稱"

//比如接口名為:java.sql.Driver,

//那么文件路徑就是:"META-INF/services/java.sql.Driver"

StringfullName=PREFIX+service.getName();

//沒(méi)有指定ClassLoader,就通過(guò)getSystemClassLoader()加載目標(biāo)文件

if(loader==null){

configs=ClassLoader.getSystemResources(fullName);

}elseif(loader==ClassLoaders.platformClassLoader()){

//如果是platformClassLoader,它沒(méi)有classpath,那么看看BootLoader有沒(méi)有classpath

if(BootLoader.hasClassPath()){

configs=BootLoader.findResources(fullName);

}else{

configs=Collections.emptyEnumeration();

}else{

//通過(guò)指定classLoader加載目標(biāo)文件

configs=loader.getResources(fullName);

}catch(IOExceptionx){

fail(service,"Errorlocatingconfigurationfiles",x);

//上面代碼只會(huì)執(zhí)行一次,這樣configs就不會(huì)為null,下次進(jìn)來(lái)直接取下一個(gè)實(shí)現(xiàn)類

//把configs內(nèi)容解析成一個(gè)迭代器

while((pending==null)||!pending.hasNext()){

if(!configs.hasMoreElements()){

returnnull;

pending=parse(configs.nextElement());

//通過(guò)迭代器獲取下一個(gè)實(shí)現(xiàn)類名稱

Stringcn=pending.next();

try{

//通過(guò)類名反射成Class對(duì)象

returnClass.forName(cn,false,loader);

}catch(ClassNotFoundExceptionx){

fail(service,"Provider"+cn+"notfound");

returnnull;

1.實(shí)現(xiàn)類的載入是因?yàn)樵贛ETA-INF/services/文件夾中創(chuàng)建了以目標(biāo)接口名稱命名的文件,并在里面寫(xiě)上了實(shí)現(xiàn)類的全路徑類名。

2.ServiceLoader通過(guò)ClassLoader從classpath中載入目標(biāo)文件里面的內(nèi)容,并解析出實(shí)現(xiàn)類的全路徑類名;

3.最終通過(guò)反射的方式創(chuàng)建出實(shí)現(xiàn)類的Class對(duì)象,這樣就完成了SPI的實(shí)現(xiàn);

SPI在各個(gè)框架上的應(yīng)用

除了在數(shù)據(jù)庫(kù)Driver上使用了SPI,我們還可以發(fā)現(xiàn)SPI在各個(gè)框架上都有大量的應(yīng)用。比如我最近在看的Seata分布式事務(wù)框架,里面就有用到SPI:mon.loader.EnhancedServiceLoader

另一個(gè)就是我們經(jīng)常使用的mysql-connector-java以及阿里的Druid:

小結(jié)

通過(guò)以上源碼分析以及示例演示,我們簡(jiǎn)單做一個(gè)小結(jié):

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論