記一次 JDK SPI 設定不生效的問題 → 這麼簡單都不會,還是回家養豬吧

2022-05-30 12:07:52

開心一刻

  今天去幼兒園接小侄女,路上聊起了天

  小侄女:小叔,今天我吃東西被老師發現了

  我:老師說了什麼

  小侄女:她說拿出來,跟小朋友一起分享

  我:那你拿出來了嗎

  小侄女一臉可憐的看向我,說道:沒有,我沒有那麼多鼻屎

SPI

  概念

  SPI 全稱 Service Provider Interface ,直譯過來就是: 服務提供介面 ,是不是有點抽象?

  簡單點理解,Java SPI 就是提供這樣的一個機制:為某個介面尋找服務實現的機制

  還是抽象?我們往下看它的具體實現就好理解了

  實現三板斧

  1、介面與實現

    Animal 介面

    Dog 實現

    Cat 實現

  2、組態檔

    組態檔有點講究,需要按這套規則來

    2.1 在 src/main/resources/ 下建立目錄: /META-INF/services ,位置和名字都必須嚴格按這個來,一字都不能差

    2.2 在 /META-INF/services 目錄下建立一個以介面全限定類名為名的檔案: com.qsl.service.Animal ,沒有額外的字尾

    2.3 將介面實現類的全限定類名寫入到 2.2 建立的檔案中,一個實現佔一行

  3、ServiceLoader 載入

    通過 ServiceLoader 進行載入,程式碼很簡單,如下所示

    正常情況下會輸出如下內容

  範例工程結構如下

  至此,對 SPI 的感覺是不是沒那麼抽象了

  簡單理解, Java SPI 是 基於介面的程式設計 + 策略模式 + 組態檔 實現的動態載入機制

  使用場景

  不太好概括,一千個人眼中有一千個哈姆雷特

  但是我們可以通過一些案例來形成自己的概括

  1、DriverManager

    不知道大家還記得 JDBC 的寫法嗎

    我們去跟下 DriverManager 的原始碼

    我們再看下 MySQL 驅動的包結構

  2、SLF4J

    具體原始碼我就不帶大家去跟了,有興趣的可以去看看:從原始碼來理解slf4j的繫結,以及logback對組態檔的載入 中的問題1

  3、Spring SPI

     Spring 有自己的 SPI 實現機制,和 JDK SPI 略有不同

     Spring 是在 src/main/resources/META-INF 目錄下建立 spring.factories ,裡面以鍵值對的方式存放多個實現,類似如下

  4、Dubbo SPI

     Dubbo 又有自己的一套實現,組態檔需要放到 META-INF/dubbo 目錄下

    具體細節可檢視其官方檔案:Dubbo SPI

問題重現

  此刻,大家是不是覺得 JDK SPI 很簡單?

  但正是這麼簡單的東西,樓主都碰到了問題,如下圖所示

  當時人就懵了!!!

問題排查

  一度懷疑是不是 JDK SPI 還有額外的設定

  因為是工作中的專案出了這個問題,所以我自建了一個 demo 來驗證 實現三板斧 

  結果 demo 的執行是沒問題的,這也就說明 JDK SPI 的實現就只有那三板斧,那問題出在哪了?

  本著快速解決問題的目的,我換了一種實現方式,採用 Spring SPI 

  結果依然是有問題,同樣是讀不到 spring.factories 中的設定

  正在一籌莫展之際,直覺告訴我是不是 maven 構建出了問題,所以我對專案進行了 package ,然後去看了下打好的包的目錄結構

   META-INF 目錄下的 com.qsl.service.Animal 檔案了?

  肯定是 pom.xml 設定不對

  我是萬萬沒想到 pom.xml 會進行如上的設定(後面問了老同事,沒特別的原因,就是簡單的認為只會有 xml 和 yml 組態檔)

  此刻,相信大家都知道怎麼改了吧(去掉<includes>標籤,或者在<includes>中加上)

  然而樓主沒用採用上述兩種方案的任一一個,也沒有改 pom.xml ,就問你氣不氣?

總結

  1、 JDK SPI 的使用,就那三板斧,如果出了問題,不用想,肯定不是 JDK SPI 的問題

  2、關於 SPI 的使用場景,樓主仍然不做概括(太菜,概括不好),大家自行去概括

  3、關於 pom.xml 

    樓主之前寫過一篇:Maven pom.xml中的元素modules、parent、properties以及import

    但就是沒講 <build> ,下次補上,你們記得提醒我哦!