字節(jié)面試也會(huì)問SPI機(jī)制?
?1.前言
Java SPI 機(jī)制,主要是類加載器反雙親委派的實(shí)現(xiàn)(第三方包不在指定jdk路徑,一般類加載器無法加載,需要特殊的ContextClassLoader加載以便使用)。本次將對 SPI機(jī)制進(jìn)行詳解,并結(jié)合案例介紹其在實(shí)際場景中具體使用。
提示:以下是本篇文章正文內(nèi)容,案例僅供對比參考
2.什么是SPI機(jī)制?
- SPI(全稱:Service Provider Interface),是jdk內(nèi)置的一種服務(wù)提供發(fā)現(xiàn)接口機(jī)制,旨在由第三方服務(wù)實(shí)現(xiàn)或擴(kuò)展為組件,方便開發(fā)人員快速集成指定擴(kuò)展組件滿足指定的需求。這對于應(yīng)用或平臺(tái)擴(kuò)展來說,無疑是一種成本較低、動(dòng)態(tài)靈活的方案。
- SPI機(jī)制調(diào)度過程(業(yè)務(wù)調(diào)用方可根據(jù)加載的擴(kuò)展實(shí)現(xiàn)類實(shí)現(xiàn)功能)
- 調(diào)用流程
3.實(shí)現(xiàn)方式及使用場景
鑒于目前實(shí)際項(xiàng)目涉及范圍,總結(jié)出的常見應(yīng)用場景。
3.1 接口權(quán)限定文件名方式
即在resource文件下創(chuàng)建META/services/目錄,并在此目錄下新建文件,文件名稱為接口類權(quán)限定文件名,如 com.lgy.spidemo.serviceway.SpiService。(不好理解就是接口類的package地址 + 接口類名)
使用場景一:
- 場景描述:不同部門類型的員工需要從不同的考勤應(yīng)用獲取出勤信息,如職能部門僅拉取釘釘考勤,業(yè)務(wù)部門需要拉取釘釘考勤的基礎(chǔ)上再結(jié)合自研考勤模塊數(shù)據(jù)匯總出勤結(jié)果。
- 實(shí)現(xiàn)方式:抽象通用拉取考勤接口,定義不同部門人員考勤統(tǒng)計(jì)實(shí)現(xiàn)類。
- 直接上代碼:
- 通用接口:
package com.lgy.spidemo.serviceway;
/**
* @description: 考勤接口
**/
public interface AttendanceService {
void pullAttendanceInfos();
}
- 職能部門考勤實(shí)現(xiàn)類;
/**
* @description: 職能部門考勤實(shí)現(xiàn)
**/
public class FunctionAttendanceServiceImpl implements AttendanceService {
@Override
public void pullAttendanceInfos() {
System.out.println(" FunctionAttendanceService implements ...");
// 邏輯忽略
}
}
- 銷售部門考勤實(shí)現(xiàn);
/**
* @description: 銷售部門考勤實(shí)現(xiàn)
**/
public class SaleAttendanceServiceImpl implements AttendanceService {
@Override
public void pullAttendanceInfos() {
System.out.println(" SaleAttendanceService implements ...");
// 邏輯忽略
}
}
- 測試類;
/**
* 1、項(xiàng)目的\src\main\resources\下創(chuàng)建\META-INF\services目錄
* 2、META-INF\services的目錄下再增加一個(gè)配置文件,這個(gè)文件必須以接口的全限定類名保持一致 (com.lgy.spidemo.service.SpiService)
* 3、在配置文件中寫入具體實(shí)現(xiàn)類的全限定類名,如有多個(gè)便換行寫入 com.lgy.spidemo.service.impl.SaleAttendanceServiceImpl
com.lgy.spidemo.service.impl.FunctionAttendanceServiceImpl
**/
public class AttendanceServiceTest {
public static void main(String[] args) {
ServiceLoader<AttendanceService> services =
ServiceLoader.load(AttendanceService.class);
// 省略判斷人員部門類型邏輯
// 測試輸出結(jié)果,展示實(shí)現(xiàn)接口已加載
for (AttendanceService service : services) {
service.pullAttendanceInfos();
}
}
}
- 測試結(jié)果如下;
// 兩個(gè)實(shí)現(xiàn)類均被加載成功,在實(shí)際使用時(shí),可根據(jù)需要去調(diào)用不同的實(shí)現(xiàn)。
FunctionAttendanceService implements ...
SaleAttendanceService implements ....
實(shí)現(xiàn)類不要標(biāo)注任何注解,不然Spring在初始化過程中掃描并加載,無法測試。
結(jié)合場景一分析:
- 此場景可以通過自定義實(shí)現(xiàn)類的方式滿足業(yè)務(wù)需求(不同部門的考勤規(guī)則),有助于業(yè)務(wù)實(shí)現(xiàn)快速迭代,同時(shí)也提升了服務(wù)架構(gòu)的可拓展性。
- 考慮公司組織架構(gòu)比較復(fù)雜,部門職責(zé)分的比較細(xì),后續(xù)擴(kuò)展幾率比較大,比如職能部門行政類和運(yùn)營類標(biāo)準(zhǔn)細(xì)分,很可能會(huì)增加除了考勤之外的各種考核指標(biāo)等,借鑒此方案可能簡單實(shí)現(xiàn)并比較方便集成,使得業(yè)務(wù)間減少依賴,實(shí)現(xiàn)解耦的設(shè)計(jì)模式,因此個(gè)人是比較偏向用此方案。
- 其它應(yīng)用:如項(xiàng)目中常用的日志也是采用SPI機(jī)制,常見的common-logging的LogFatory就是標(biāo)準(zhǔn)SPI接口,有興趣的可以自行研究。
3.2 spring.factories方式
- 和上面一樣,需要在resource文件下創(chuàng)建META/services/目錄,并在此目錄下新建文件,區(qū)別在于文件名為spring.factories。
使用場景二
- 場景描述:針對于不同的開發(fā)端使用習(xí)慣展示不同的接口文檔,比如APP端習(xí)慣于Swagger,JAVA端喜歡dateway風(fēng)格,就在不同實(shí)例展示不同接口文檔。此場景是我臆想出來。
- 實(shí)現(xiàn)方式:構(gòu)建兩種版本的jar包,比如 1.0.0-swagger 、2.0.0-dataway,再對應(yīng)的包內(nèi)配置spring.factories內(nèi)的config配置類。
代碼如下:
package com.lgy.spidemo.factoriesway;
import org.springframework.boot.autoconfigure.AutoConfigurationImportEvent;
import org.springframework.boot.autoconfigure.AutoConfigurationImportListener;
/**
* @description: 自動(dòng)配置swagger
**/
public class SwaggetAutoConfiguration {
public SwaggetAutoConfiguration() {
System.out.println(" SwaggetAutoConfiguration init ...");
}
// 配置內(nèi)容省略
}
/**
* @description: 自動(dòng)配置dataway
**/
public class DataWayAutoConfiguration {
public DataWayAutoConfiguration() {
System.out.println(" DataWayAutoConfiguration init ...");
}
// 配置內(nèi)容省略
}
/**
* resource/META-INFO/spring.factories 文件內(nèi)容 *
* org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.lgy.spidemo.factoriesway.SwaggetAutoConfiguration
* 輸出結(jié)果:SwaggetAutoConfiguration init ...
**/
- 根據(jù)spring.factories內(nèi)配置的類,在springboot啟動(dòng)初始化過程中會(huì)自動(dòng)加載對應(yīng)的配置,實(shí)現(xiàn)所需的接口文檔。
結(jié)合場景二分析:
- spring.factories實(shí)現(xiàn)機(jī)制與上述方式一致,只是實(shí)現(xiàn)方式不同,本質(zhì)目的是通過抽象化類的方式,實(shí)現(xiàn)解耦,最終便于擴(kuò)展
- 其它使用場景:如spring-boot-autoconfigure-x.x.x.RELEASE.jar,就是通過此方式完成初始化加載。
4.總結(jié)
本次講解的兩種方式均是基于SPI機(jī)制,可見是多么受開發(fā)追捧。當(dāng)然,還有很多種實(shí)現(xiàn)方式,我個(gè)人覺得最主要的還是能夠在自己的掌控范圍內(nèi)去使用,畢竟有問題可以通過自己的學(xué)習(xí)理解去解決。
最后說一句,沒有更好的技術(shù)知識(shí),只有更適合的技術(shù)應(yīng)用,結(jié)合實(shí)際,檢出真理。