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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java SPI 源码解析及 demo 讲解

發布時間:2025/3/20 java 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java SPI 源码解析及 demo 讲解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

??點擊上方?好好學java?,選擇?星標?公眾號

重磅資訊、干貨,第一時間送達 今日推薦:Java實現QQ登錄和微博登錄個人原創+1博客:點擊前往,查看更多 作者:JlDang 來源:https://segmentfault.com/a/1190000022101812

1. SPI是什么,有什么用處

SPI全稱S而vice Provider Interface,是java提供的一套用來被第三方實現或者擴展的API,它可以用來啟用框架擴展和替換組件。

系統設計的各個抽象,一般有很多不同的實現方案,比如通過不同類型的配置文件加載配置信息,通過不同的序列化方案實現序列化。一般推薦模塊之間基于接口編程,模塊之間不對實現類進行硬編碼。一旦代碼里涉及具體的實現類,就違反了可插拔的原則。為了實現在模塊裝配的時候能不在程序里動態指明,這就需要一種服務發現機制。

SPI的核心思想就是解耦

2.使用場景

調用者根據實際需要替換框架的實現策略。

比如常見的例子:

  • 數據庫驅動加載類接口實現類的加載,JDBC加載不同類型數據庫的驅動

  • 日志實現類加載

  • dubbo中也大量使用SPI的方式實現框架的擴展,不過它對java提供的SPI進行了封裝

3.如何使用

實例代碼

  • 定義一組接口,并寫出多個實現類

  • package com.djl.test.spi.api;/*** @ClassName Robot* @Description TODO* @Author djl* @Date 2020-03-18 19:01* @Version 1.0*/ public interface Robot {void sayHello(); } package com.djl.test.spi.java;import com.djl.test.spi.api.Robot;/*** @ClassName Bumblebee* @Description TODO* @Author djl* @Date 2020-03-18 19:03* @Version 1.0*/ public class Bumblebee implements Robot {@Overridepublic void sayHello() {System.out.println("Hello ,I am Bumble bee");} } package com.djl.test.spi.java;import com.djl.test.spi.api.Robot;/*** @ClassName OptimusPrime* @Description TODO* @Author djl* @Date 2020-03-18 19:02* @Version 1.0*/ public class OptimusPrime implements Robot {@Overridepublic void sayHello() {System.out.println("Hello,I am Optimus prime");} }
  • 在META-INF/services文件下,創建一個以接口全限定名命名的文件,內容為實現類的全限定名

  • image.pngcom.djl.test.spi.java.Bumblebee com.djl.test.spi.java.OptimusPrime
  • 通過ServiceLoader類進行加載

  • import com.djl.test.spi.api.Robot; import com.djl.test.spi.api.RobotDubbo; import org.apache.dubbo.common.extension.ExtensionLoader; import org.junit.Test;import java.util.ServiceLoader;/*** @ClassName JavaSPITest* @Description TODO* @Author djl* @Date 2020-03-18 19:06* @Version 1.0*/ public class JavaSPITest {@Testpublic void sayHello(){ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class);System.out.println("java spi");serviceLoader.forEach(Robot::sayHello);} }
  • 執行結果

  • image.png

    4 源碼解析

    我會將分析都寫在代碼注釋中,大家可以打開自己的源碼耐心的看一會。

    接下來是重頭戲了,知道了spi怎么用,那么內部是如何實現的呢?

    我們直接從ServiceLoader類的load方法看起。

    /**為給定的服務類型創建一個新的服務加載程序,使用 *當前線程的{@linkplain java.lang.Thread#getContextClassLoader *上下文類裝入器}。 */ public static <S> ServiceLoader<S> load(Class<S> service) {//1.獲取當前線程的類加載ClassLoader cl = Thread.currentThread().getContextClassLoader();return ServiceLoader.load(service, cl); }</code><code class="java">public static <S> ServiceLoader<S> load(Class<S> service,ClassLoader loader) {//new一個serviceloader對象return new ServiceLoader<>(service, loader); }</code><code class="java">//構造函數 private ServiceLoader(Class<S> svc, ClassLoader cl) {//判斷入參是否為nullservice = Objects.requireNonNull(svc, "Service interface cannot be null");//2.加載器如果不存在,獲取系統類加載器,通常是applicationLoader,應用程序加載器loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;//3.獲取訪問控制器acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;reload(); }</code><code class="java">public void reload() {// 清空緩存providers.clear();// 初始化內部類,用于遍歷提供者lookupIterator = new LazyIterator(service, loader); }

    看到這里,相信大家對于初始化的內容有了一定了解,這里面涉及到了一些屬性,我們來總結下

    private static final String PREFIX = "META-INF/services/";// 要加載的類 private final Class<S> service;// 用于加載實現類的類加載器 private final ClassLoader loader;// 訪問控制器 private final AccessControlContext acc;// 提供者的緩存 private LinkedHashMap<String,S> providers = new LinkedHashMap<>();// 一個內部類,用于遍歷實現類 private LazyIterator lookupIterator;

    現在我們發現重點就在于LazyIterator這個內部類上,我們獲取實現類都看這個內部類了,我們繼續來分析

    private class LazyIteratorimplements Iterator<S> {Class<S> service;ClassLoader loader;Enumeration<URL> configs = null;Iterator<String> pending = null;String nextName = null;//構造函數private LazyIterator(Class<S> service, ClassLoader loader) {this.service = service;this.loader = loader;}private boolean hasNextService() {if (nextName != null) {return true;}if (configs == null) {try {//獲取META-INF/services下文件全稱String fullName = PREFIX + service.getName();if (loader == null)configs = ClassLoader.getSystemResources(fullName);else//獲取配置文件內具體實現的枚舉類configs = loader.getResources(fullName);} catch (IOException x) {fail(service, "Error locating configuration files", x);}}while ((pending == null) || !pending.hasNext()) {if (!configs.hasMoreElements()) {return false;}pending = parse(service, configs.nextElement());}nextName = pending.next();return true;}</code><code class="java">private Iterator<String> parse(Class<?> service, URL u)throws ServiceConfigurationError {InputStream in = null;BufferedReader r = null;//存儲配置文件中實現類的全限定名ArrayList<String> names = new ArrayList<>();try {in = u.openStream();r = new BufferedReader(new InputStreamReader(in, "utf-8"));int lc = 1;//讀取文件內容,這里不多說了,正常的流操作while ((lc = parseLine(service, u, r, lc, names)) >= 0);} catch (IOException x) {fail(service, "Error reading configuration file", x);} finally {try {if (r != null) r.close();if (in != null) in.close();} catch (IOException y) {fail(service, "Error closing configuration file", y);}}return names.iterator(); }</code><code class="java">private S nextService() {if (!hasNextService())throw new NoSuchElementException();//循環遍歷獲取實現類的全限定名String cn = nextName;nextName = null;Class<?> c = null;try {//實例化實現類c = Class.forName(cn, false, loader);} catch (ClassNotFoundException x) {fail(service,"Provider " + cn + " not found");}if (!service.isAssignableFrom(c)) {fail(service,"Provider " + cn + " not a subtype");}try {//這一行將實例化的類強轉成所表示的類型S p = service.cast(c.newInstance());//緩存實現類providers.put(cn, p);//返回對象return p;} catch (Throwable x) {fail(service,"Provider " + cn + " could not be instantiated",x);}throw new Error(); // This cannot happen } image.png//這里是iterable循環遍歷 default void forEach(Consumer<? super T> action) {Objects.requireNonNull(action);for (T t : this) {action.accept(t);} }

    到這里整個鏈路就分析完成了。

    感興趣的小伙伴可以按照demo,自己跑一遍,有問題歡迎提問。

    總結

    以上是生活随笔為你收集整理的Java SPI 源码解析及 demo 讲解的全部內容,希望文章能夠幫你解決所遇到的問題。

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

    主站蜘蛛池模板: 不卡av一区 | 香蕉视频黄色在线观看 | 欧美aaaaaaaaa | 亚洲av无码专区在线 | 国产欧美一区二区三区精华液好吗 | 久久久久久久久久福利 | 国产精品国产三级国产专播品爱网 | 女人高潮潮呻吟喷水 | 四虎影视免费 | 激情婷婷在线 | 91免费成人 | 亚洲av无码一区二区三区观看 | 全程偷拍露脸中年夫妇 | 日本成人一区二区 | 色94色欧美sute亚洲线路二 | 337p粉嫩大胆噜噜噜亚瑟影院 | 国产一级特黄aaa大片 | 日本少妇xxxx | 婷婷干 | 男女草逼视频 | 91麻豆蜜桃一区二区三区 | 国产一区二区三区免费在线观看 | 免费的毛片网站 | 美女裸体跪姿扒开屁股无内裤 | 亚洲精品国产精品乱码桃花 | 久久久水蜜桃 | 永久免费毛片 | 亚洲免费婷婷 | 一及黄色大片 | 羞羞在线观看 | 天天爽天天爽天天爽 | 国产成人在线免费视频 | 国产超碰人人爽人人做人人爱 | 88国产精品视频一区二区三区 | 国产欧美一区二区三区免费看 | 大肉大捧一进一出好爽 | 播播成人网 | 成人免费xxxxx在线视频 | 超碰在线cao | 欧美三级视频在线 | h片在线观看免费 | 国产无遮挡18禁无码网站不卡 | 97综合网| 木木影院 | 国产又爽又黄视频 | 国产香蕉一区二区三区 | 日韩久久不卡 | 国产正在播放 | 亚洲网址在线 | 国产十区 | 性色AV无码久久一区二区三 | 青青青在线视频观看 | 亚洲va中文字幕 | 久久久电影 | 亚洲2022国产成人精品无码区 | 性欧美在线观看 | 久久毛片网站 | 涩久久| 巨大乳の揉んで乳榨り奶水 | 91成人免费在线 | av影片在线播放 | 国产免费脚交足视频在线观看 | 校园春色亚洲色图 | 日韩福利视频 | 天降女子在线观看 | 少妇高潮网站 | 另类av小说 | 久久天堂视频 | av观看国产 | 亚洲av电影一区二区 | 无码无套少妇毛多18pxxxx | 天堂在线观看免费视频 | av网子| 成年人久久 | 欧美综合色区 | 国产黄色免费观看 | 欧美日韩一级二级 | 狠狠干干干 | 99视频免费看 | 手机看片日韩久久 | 亚洲一区二区三区四区五区午夜 | 亚洲美免无码中文字幕在线 | 朝桐光一区二区三区 | 日本一区视频在线播放 | 99久免费精品视频在线观78 | 日韩美女视频在线观看 | 少妇人禽zoz0伦视频 | av免费在线观看网址 | 亚洲av片一区二区三区 | 亚洲字幕在线观看 | 久久久视频在线观看 | 日韩激情床戏 | 亚洲天堂视频在线播放 | av一级大片| 无码人妻熟妇av又粗又大 | 水果视频污 | 天天干,天天操,天天射 | 爱豆国产剧免费观看大全剧集 | www.youjizz国产|