日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

serviceloader java_【java编程】ServiceLoader使用看这一篇就够了

發布時間:2023/12/4 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 serviceloader java_【java编程】ServiceLoader使用看这一篇就够了 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載:https://www.jianshu.com/p/7601ba434ff4

想必大家多多少少聽過spi,具體的解釋我就不多說了。但是它具體是怎么實現的呢?它的原理是什么呢?下面我就圍繞這兩個問題來解釋:

實現: 其實具體的實現類就是java.util.ServiceLoader這個類。

要想了解一個機制的原理,首先得知道它是怎么運行的,需要什么配置,才能運行起來。然后再分解來了解實現。對于技術實現也是一樣,先看這個類是怎么實現的,先讓它跑起來,看到效果。然后再講原理。

按照使用說明文檔,應該分下面幾個步驟來使用:

創建一個接口文件

在resources資源目錄下創建META-INF/services文件夾

在services文件夾中創建文件,以接口全名命名

創建接口實現類

我們想測試一下,一般是在這個工程中建立一個測試類來測試。來看下代碼片段:

接口類

public interfaceIMyServiceLoader {

String sayHello();

String getName();

}

View Code

實現類:

public class MyServiceLoaderImpl1 implementsIMyServiceLoader {

@OverridepublicString sayHello() {return "hello1";

}

@OverridepublicString getName() {return "name1";

}

}public class MyServiceLoaderImpl2 implementsIMyServiceLoader {

@OverridepublicString sayHello() {return "hello2";

}

@OverridepublicString getName() {return "name2";

}

}

View Code

測試類:

public classTestMyServiceLoader {public static voidmain(String[] argus){

ServiceLoader serviceLoader = ServiceLoader.load(IMyServiceLoader.class);for(IMyServiceLoader myServiceLoader : serviceLoader){

System.out.println(myServiceLoader.getName()+myServiceLoader.sayHello());

}

}

}

View Code

正常情況下這里應該輸出

name2hello2

name1hello1

View Code

看了這些步驟,想必你也知道原理了,我在這里總結下。

原理:在ServiceLoader.load的時候,根據傳入的接口類,遍歷META-INF/services目錄下的以該類命名的文件中的所有類,并實例化返回。

相信看到這里,有的看客該爆粗話了,說啥子看著一篇就夠了,這些知識點隨便一搜,到處都是好伐。是的,上面說的,確實隨便一搜都可以搜到,所以這里我要劃重點了:

一、問題

上面說了,正常情況下會那樣輸出,但是你運行程序你就會發現,馬丹,怎么不起作用啊,我哪里做錯了,都是按照文章步驟來做的。弄的你都開始懷疑人生了。不要懷疑人生,在一個工程中做測試,確實不能實現想要的效果。

二、回憶場景

回憶一下spi的使用場景。它是給制作標準的一放用的,用來指定標準,然后不同實現方,用不同的方式實現標準供使用方使用。那標準方和實現方必然不是一個。想到這里,你應該能夠向明白了吧。

三、解決方案

要解決問題,就把之前做的打jar包,引入新工程測試,這樣就可以了。

四、疑問

但是有人會說標準方和實現方也可能是一個啊,好比標準方我提供一個內部的實現方案也是可以的啊。也確實有道理啊,那這種怎么實現呢?

五、思考

當然也有辦法,下面就說下實現方法。想要實現上面的需求,首先要知道攔阻這個需求實現的問題,然后把這些問題都解決了,需求自然也就實現了。那就先來分析問題吧,為什么在一個工程中獲取不到接口的實現類呢?經過觀察發現是因為資源文件沒有在classPath中,為什么這么說呢,可以看下build的目錄下面是沒有META-INF文件夾。現在知道了原因,這么解決呢?

六、疑問臨時解決方案

最簡單的方法,把資源下的META-INF文件夾拷貝到build目錄下,然后再運行,發現可以了,這也就驗證了,確實是這個問題造成的。搞定!

七、再次發出疑問

這樣就結束了,那我總不能手動拷貝吧,這不算解決方案,只是臨時方案。那要怎么解決呢?

我就不賣關子了。其實要解決這個問題,只要在編譯的時候把這些文件放到build目錄中就行了,是不是很簡單。思路是有了,可是怎么實現呢?這個時候要用到攔截編譯處理,然后再里面做這件事情。

方案一

繼承AbsStractProcessor,在process方法中把資源文件移到build目錄下。

方案二

這里用到了google開源的AutoService

大概看了下autoService的源碼,其實它也是使用方案一的方法,攔截編譯過程,然后再build目錄下生成配置文件,這里來大概看下它的process方法:

ublic boolean process(Set extends TypeElement>annotations, RoundEnvironment roundEnv) {try{returnprocessImpl(annotations, roundEnv);

}catch(Exception e) {

...return true;

}

}private boolean processImpl(Set extends TypeElement>annotations, RoundEnvironment roundEnv) {if(roundEnv.processingOver()) {

generateConfigFiles();

}else{

processAnnotations(annotations, roundEnv);

}return true;

}

View Code

這里你會發現其實就是generateConfigFiles()和processAnnotations(annotations, roundEnv)看名字可以猜到processAnnotations是處理注解的,這里實現類都實現了注解,所以這里應該是找到實現類。

rivate void processAnnotations(Set extends TypeElement>annotations,

RoundEnvironment roundEnv) {

Set extends Element> elements = roundEnv.getElementsAnnotatedWith(AutoService.class);for(Element e : elements) {

TypeElement providerImplementer=(TypeElement) e;

AnnotationMirror providerAnnotation= getAnnotationMirror(e, AutoService.class).get();

DeclaredType providerInterface=getProviderInterface(providerAnnotation);

TypeElement providerType=(TypeElement) providerInterface.asElement();

...

String providerTypeName=getBinaryName(providerType);

String providerImplementerName=getBinaryName(providerImplementer);

providers.put(providerTypeName, providerImplementerName);

}

}

View Code

確實如此,這里會把所有的實現類存起來。

再來看看generateConfigFiles()方法

private voidgenerateConfigFiles() {

Filer filer=processingEnv.getFiler();for(String providerInterface : providers.keySet()) {

String resourceFile= "META-INF/services/" +providerInterface;try{

SortedSet allServices =Sets.newTreeSet();try{

FileObject existingFile= filer.getResource(StandardLocation.CLASS_OUTPUT, "",

resourceFile);

Set oldServices =ServicesFiles.readServiceFile(existingFile.openInputStream());

allServices.addAll(oldServices);

}catch(IOException e) {

}

Set newServices = new HashSet(providers.get(providerInterface));

allServices.addAll(newServices);

FileObject fileObject= filer.createResource(StandardLocation.CLASS_OUTPUT, "",

resourceFile);

OutputStream out=fileObject.openOutputStream();

ServicesFiles.writeServiceFile(allServices, out);

out.close();

}catch(IOException e) {return;

}

}

}

View Code

這里是在build下創建META-INF目錄。和我們想的一模一樣。

八、總結

好了,要實現文章開頭的需求,除非你覺得你比google開源AutoService的工程師寫的更好,不然就直接使用AutoService吧。這篇文章不僅是分析ServiceLoader的原理,實現我們的需求,更重要的是高速我們遇到問題該怎么分析問題,解決問題。

九、擴展

其實還有很多比較好玩的,比如在攔截到編譯過程時,可以再編譯期生成一些有意思的代碼,來幫我們實現一些自動化處理。這就需要動用我們的大腦就想了,介紹一下生成代碼的庫javapoet,大家可以了解一下。

總結

以上是生活随笔為你收集整理的serviceloader java_【java编程】ServiceLoader使用看这一篇就够了的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。