serviceloader java_【java編程】ServiceLoader使用看這一篇就夠了

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

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

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

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

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

創建一個接口文件

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

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

創建接口實現類

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

接口類

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

public interfaceIMyServiceLoader {

String sayHello();

String getName();

}

View Code

實現類:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

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

測試類:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

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

正常情況下這里應該輸出

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

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方法:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

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是處理注解的,這里實現類都實現了注解,所以這里應該是找到實現類。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

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()方法

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

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,大家可以了解一下。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/287504.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/287504.shtml
英文地址,請注明出處:http://en.pswp.cn/news/287504.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

.NET7 Preview4 之OpenAPI swagger改進

在MiniAPI系列中,《.NET6之MiniAPI(十八):OpenAPI swagger》介紹了swagger在MiniAPI框架中的使用,當時留下很多不足,隨著.NET7 Preview4的推出,這方面得到了很大的改進,我還是使用“十八”這篇文章的案例。…

Swift - 自定義單元格實現微信聊天界面

1,下面是一個放微信聊天界面的消息展示列表,實現的功能有: (1)消息可以是文本消息也可以是圖片消息(2)消息背景為氣泡狀圖片,同時消息氣泡可根據內容自適應大小(3&#x…

[python opencv 計算機視覺零基礎到實戰] 十三 直方圖顏色提鮮

一、學習目標 了解了均衡化的作用是什么了解灰度、YUV、彩色圖片均衡化的方法是使用什么方法了解了合并通道的方法是什么了解了分離通道的方法是什么 如有錯誤歡迎指出~ 二、了解圖像均衡化 2.1 了解直方圖均衡化 圖像直方圖均衡化主要是對圖像中的少數灰度進行壓縮&#…

java 中字符串比較方法_java中常用的字符串的比較方法(兩種)

比較字符串比較常用的兩個方法是運算符“”和String的equals方法。使用“”比較兩個字符串,是比較兩個對象的的“地址”是否一致,本質就是判斷兩個變量是否指向同一個對象,如果是則返回true,否則返回的是false。而String類的equal…

Android之稍微靠譜點的透明Activity(不獲取觸摸事件)

1 問題 實現透明的Activity(不獲取觸摸事件),就行什么也看不到,打開了透明activity,也不影響其他頁面的滑動和點擊,就行什么事情都沒發生一樣。 2 代碼實現 1)配置樣式 <style name="TestTheme" parent="Theme.AppCompat.Light"><item na…

分布式服務框架dubbo原理解析 轉

alibaba有好幾個分布式框架&#xff0c;主要有&#xff1a;進行遠程調用(類似于RMI的這種遠程調用)的(dubbo、hsf)&#xff0c;jms消息服務(napoli、notify)&#xff0c;KV數據庫(tair)等。這個框架/工具/產品在實現的時候&#xff0c;都考慮到了容災&#xff0c;擴展&#xff…

【傾情奉獻】遙感物候研究:30年長時間序列遙感數據集GIMMS 3g NDVI產品預處理完整步驟

本文為作者碩士學位論文遙感物候研究數據處理過程總結。GIMMS(Global Inventory Modelling and Mapping Studies) 3g NDVI來源于ECOCAST網站(http://ecocast.arc.nasa.gov),是由NOAA衛星搭載的AVHRR傳感器獲取的全球植被數據,空間分辨率為0.0833?,時間分辨率為15?d,一…

過早的給方法中 引用對象 設為 null 可被 GC提前回收嗎?

經常在代碼中看到有人將 null 賦值給引用類型&#xff0c;來達到讓 GC 提前回收的目的&#xff0c;這樣做真的有用嗎&#xff1f;今天我們就來研究一下。為了方便講解&#xff0c;來一段測試代碼&#xff0c;提前將 test1null &#xff0c;然后調用 GC.Collect() 看看是否能提前…

[python opencv 計算機視覺零基礎到實戰] 十五 直方圖反向投影

一、學習目標 了解了直方圖反向投影的一般流程了解2D直方圖的使用 如有錯誤歡迎指出~ 二、了解直方圖反向投影 2.1 了解2D直方圖 需要對直方圖進行反向投影&#xff0c;需要使用2D直方圖。2D直方圖需要使用calcHist方法。calcHist方法在前兩節中已經有了解&#xff0c;現在…

關聯規則java代碼_重量挖掘關聯規則挖掘方法,哪個大神可以將以下偽代碼轉換為Java代碼?...

重量挖掘關聯規則挖掘方法&#xff0c;哪個大神可以將以下偽代碼轉換為Java代碼&#xff1f; 10改進的加權關聯規則算法的基本步驟與Apriori算法相似: 首先找到加權支持度不小于用戶指定的最小加權支持度的所有頻繁項集加權關聯規則&#xff0c;然后使用頻繁項集生成所有滿足最…

Boostrap ZURB Foundation —— Web開發前端框架

webflow&#xff1a;Webflow 允許設計師通過自由的拖拉拽與 CSS 類互動&#xff0c;而定義它們的過程無需寫任何一行代碼。用戶在完成從設計到 CSS 構架之后&#xff0c;甚至可以在線直接將建好的網頁發布&#xff0c;而不需要導出代碼到其他發布工具上。類似的這些 B2D 市場&a…

Git之HEAD和origin

1 問題 我們經常看見git相關操作里面看到HEAD和origin這些專業名稱&#xff0c;它娘的到底什么意思。 2 解釋 1&#xff09;HEAD git 中的分支&#xff0c;本質上僅僅是個指向 commit 對象的可變指針&#xff0c; HEAD 是一個特別指針&#xff0c;它是一個指向你正在工作中的…

如何離線安裝chrome插件

如何離線安裝chrome插件 本文轉自Work Hard Work Smart博客園博客&#xff0c;原文鏈接&#xff1a;http://www.cnblogs.com/linlf03/p/6838852.html&#xff0c;如需轉載請自行聯系原作者

多種語言《九九乘法表》薈萃:C、C++、C#、JavaScript、SQL、VB、VBA、Python

九九乘法表對于我們學習循環結構,尤其是雙重循環特別有幫助,本文演示用C、C++、C#、HTML、SQL、VB、VBA、Python等多種語九九乘法表。 一、C語言 #include<stdio.h> main() {int i,j;for(i=1;i<=9;i++){for(j=1;j<=i;j++){printf("%d*%d=%d\t",j,i,i*j…

Git之刪除遠程分支

1 問題 在工作區間刪除遠程分支 2 刪除命令 git push origin --delete name

iptables (2) 基本配置

iptables 基本命令使用舉例 一、鏈的基本操作 1、清除所有的規則。1&#xff09;清除預設表filter中所有規則鏈中的規則。# iptables -F -F, --flush [chain]Flush the selected chain (all the chains in the table if none is given). This is equivalent to deleting all …

[python opencv 計算機視覺零基礎到實戰] 十六、用opencv畫畫

一、學習目標 了解如何使用line方法了解如何使用rectangle方法了解如何使用ellipse方法 如有錯誤歡迎指出~ 二、了解OpenCV的繪圖方法 2.1 了解直線繪圖方法 我們在前兩節中有了解使用OpenCV中的矩形繪制&#xff0c;接下來我們了解一下更多的圖形繪制方法。我們在OpenCV中…

基于.NetCore開發博客項目 StarBlog - (5) 開始搭建Web項目

系列文章基于.NetCore開發博客項目 StarBlog - (1) 為什么需要自己寫一個博客&#xff1f;基于.NetCore開發博客項目 StarBlog - (2) 環境準備和創建項目基于.NetCore開發博客項目 StarBlog - (3) 模型設計基于.NetCore開發博客項目 StarBlog - (4) markdown博客批量導入基于.N…

java計算每個元素出現的百分比_Java程序計算百分比

百分數表示百分數(百)&#xff0c;即百分數與100之比。百分數的符號為&#xff05;。我們通常會計算獲得的商標&#xff0c;投資回報率等百分比。該百分比也可以超過100&#xff05;。例如&#xff0c;假設我們有總數和一部分。所以我們說那一部分占總數的百分之幾&#xff0c;…