在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Java的SPI機(jī)制詳解

京東云 ? 來(lái)源:京東物流 楊葦葦 ? 作者:京東物流 楊葦葦 ? 2025-03-05 11:35 ? 次閱讀

作者:京東物流 楊葦葦

1.SPI簡(jiǎn)介

SPI(Service Provicer Interface)是Java語(yǔ)言提供的一種接口發(fā)現(xiàn)機(jī)制,用來(lái)實(shí)現(xiàn)接口和接口實(shí)現(xiàn)的解耦。簡(jiǎn)單來(lái)說(shuō),就是系統(tǒng)只需要定義接口規(guī)范以及可以發(fā)現(xiàn)接口實(shí)現(xiàn)的機(jī)制,而不需要實(shí)現(xiàn)接口。

SPI機(jī)制在Java中應(yīng)用廣泛。例如:JDBC中的數(shù)據(jù)庫(kù)連接驅(qū)動(dòng)使用SPI機(jī)制,只定義了數(shù)據(jù)庫(kù)連接接口的規(guī)范,而具體實(shí)現(xiàn)由各大數(shù)據(jù)庫(kù)廠商實(shí)現(xiàn),不同數(shù)據(jù)庫(kù)的實(shí)現(xiàn)不同,我們常用的mysql的驅(qū)動(dòng)也實(shí)現(xiàn)了其接口規(guī)范,通過(guò)這種方式,JDBC數(shù)據(jù)庫(kù)連接可以適配不同的數(shù)據(jù)庫(kù)。

SPI機(jī)制在各種框架中也有應(yīng)用,例如:springboot的自動(dòng)裝配中查找spring.factories文件的步驟就是應(yīng)用了SPI機(jī)制;dubbo也對(duì)Java的SPI機(jī)制進(jìn)行擴(kuò)展,實(shí)現(xiàn)了自己的SPI機(jī)制。

2.SPI入門案例

2.1.創(chuàng)建工程

我們剛才在介紹中說(shuō)過(guò)了,SPI機(jī)制需要定義接口規(guī)范,這里我們以一個(gè)簡(jiǎn)單的接口案例來(lái)說(shuō)明。

首先我們需要?jiǎng)?chuàng)建四個(gè)工程:

?spi-interface,這里定義SPI的接口類:Person

?spi-impl1,這里定義接口的第一個(gè)實(shí)現(xiàn)類:Teacher

?spi-impl2,這里定義接口的第二個(gè)實(shí)現(xiàn)類:Student

?spi-test,這里通過(guò)SPI機(jī)制加載所有實(shí)現(xiàn)類進(jìn)行測(cè)試

wKgZPGfHxpqAeAmgAAAjzFACycg642.png

??

2.2.創(chuàng)建SPI接口規(guī)范

接口如下所示:

package com.jd.spi;

public interface Person {

    String favorite();
}

2.3.創(chuàng)建實(shí)現(xiàn)類1項(xiàng)目

2.3.1.創(chuàng)建接口

接口如下所示:

package com.jd.spi;


public class Teacher implements Person {

    public String favorite() {
        return "老師喜歡給學(xué)生上課";
    }
}

2.3.2.創(chuàng)建spi配置文件

如下圖所示,在項(xiàng)目的resources文件夾下創(chuàng)建兩個(gè)文件夾META-INF/services,然后在文件夾下面創(chuàng)建名稱為com.jd.spi.Person的文件,其文件的內(nèi)容為當(dāng)前項(xiàng)目的接口實(shí)現(xiàn)類com.jd.spi.Teacher。

wKgZO2fHxpuAPo_iAADdS6KCX7c957.png

??

2.4.創(chuàng)建實(shí)現(xiàn)類2項(xiàng)目

2.4.1.創(chuàng)建實(shí)現(xiàn)類2

接口如下所示:

package com.jd.spi;

public class Student implements Person {
    public String favorite() {
        return "學(xué)生喜歡努力學(xué)習(xí)";
    }
}

2.4.2.創(chuàng)建spi配置文件

如下圖所示,在項(xiàng)目的resources文件夾下創(chuàng)建兩個(gè)文件夾META-INF/services,然后在文件夾下面創(chuàng)建名稱為com.jd.spi.Person的文件,其文件的內(nèi)容為當(dāng)前項(xiàng)目的接口實(shí)現(xiàn)類com.jd.spi.Student。

wKgZPGfHxpuAB1NEAADUuAGqK38574.png

??

2.5.創(chuàng)建測(cè)試項(xiàng)目

2.5.1.引入3個(gè)maven依賴

這里需要引入接口定義項(xiàng)目和兩個(gè)接口實(shí)現(xiàn)項(xiàng)目。

如下所示:

    
        
            org.example
            spi-interface
            1.0-SNAPSHOT
        
        
            org.example
            spi-impl1
            1.0-SNAPSHOT
        
        
            org.example
            spi-impl2
            1.0-SNAPSHOT
        
    

2.5.2.創(chuàng)建測(cè)試類

如下所示:

package com.jd.spi;

import java.util.Iterator;
import java.util.ServiceLoader;

public class SPITest {

    public static void main(String[] args) {
        ServiceLoader loader = ServiceLoader.load(Person.class);
        for(Iterator it = loader.iterator(); it.hasNext();){
            Person person = it.next();
            System.out.println(person.favorite());;
        }
    }
}

運(yùn)行測(cè)試類,其結(jié)果如下所示:

wKgZO2fHxpyAKyMLAABcra25luY964.png

??

我們發(fā)現(xiàn),Java的SPI機(jī)制獲取了所有Person類的實(shí)現(xiàn)類,并執(zhí)行其對(duì)應(yīng)的favorite方法。

3.SPI機(jī)制的原理

3.1.ServiceLoader的核心屬性

其核心機(jī)制就是ServiceLoader類的load方法,下面我們將從源碼來(lái)分析其原理。

首先我們先看下ServiceLoader的核心屬性:

public final class ServiceLoader
    implements Iterable
{

    private static final String PREFIX = "META-INF/services/";

    // The class or interface representing the service being loaded
    private final Class service;

    // The class loader used to locate, load, and instantiate providers
    private final ClassLoader loader;

    // The access control context taken when the ServiceLoader is created
    private final AccessControlContext acc;

    // Cached providers, in instantiation order
    private LinkedHashMap providers = new LinkedHashMap();

    // The current lazy-lookup iterator
    private LazyIterator lookupIterator;

這個(gè)PREFIX屬性、providers屬性和lookupIterator屬性將在后續(xù)的代碼中使用到,我們發(fā)現(xiàn)PREFIX屬性就是示例中說(shuō)的META-INF/services路徑。

3.2.ServiceLoader的遍歷器

示例中,我們會(huì)獲取serviceLoader的遍歷器iterator,其方法如下所示:

    public Iterator iterator() {
        return new Iterator() {

            Iterator> knownProviders
                = providers.entrySet().iterator();

            public boolean hasNext() {
                if (knownProviders.hasNext())
                    return true;
                return lookupIterator.hasNext();
            }

            public S next() {
                if (knownProviders.hasNext())
                    return knownProviders.next().getValue();
                return lookupIterator.next();
            }

            public void remove() {
                throw new UnsupportedOperationException();
            }

        };
    }

然后需要執(zhí)行遍歷器的next方法獲取元素,其next方法執(zhí)行的是lookupIterator.next()。

接下來(lái)我們來(lái)看下lookupIterator的next方法:

        public S next() {
            if (acc == null) {
                return nextService();
            } else {
                PrivilegedAction action = new PrivilegedAction() {
                    public S run() { return nextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }

其執(zhí)行的是nextService方法,如下所示:

        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
        }

nextService方法首先執(zhí)行hasNextService方法,如下所示:

        private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                    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;
        }

這個(gè)方法會(huì)執(zhí)行String fullName = PREFIX + service.getName(),而PREFIX就是我們前面剛才說(shuō)的非常重要的屬性,其值為META-INF/services/,service就是接口類,其最終的fullName指的就是META-INF/services文件夾下的名稱為com.jd.spi.Person的文件。

接著會(huì)執(zhí)行configs = loader.getResources(fullName)方法,這個(gè)方法這里不做詳細(xì)描述,其主要功能就是獲取類路徑下所有相對(duì)路徑為fullName的所有文件的URL對(duì)象。

然后會(huì)執(zhí)行pending = parse(service, configs.nextElement())方法,這個(gè)方法這里也不詳細(xì)描述,其主要功能是讀取文件,將文件內(nèi)容變成字符串,然后nextName就被賦值為當(dāng)前文件的內(nèi)容,即實(shí)現(xiàn)類的接口全限定名

因此,執(zhí)行hasNextService()方法后,nextName被賦值為一個(gè)實(shí)現(xiàn)類的全限定名。

我們繼續(xù)看上面的nextService()方法,其最終會(huì)執(zhí)行c = Class.forName(cn, false, loader)方法,這個(gè)方法很明顯就是通過(guò)反射實(shí)例化一個(gè)對(duì)象。通過(guò)一系列操作,最終返回了對(duì)應(yīng)實(shí)現(xiàn)類的對(duì)象。

3.3.流程總結(jié)

我們將其總結(jié)為以下幾個(gè)步驟:

1.創(chuàng)建ServiceLoader對(duì)象

2.創(chuàng)建迭代器lookupIterator

3.通過(guò)迭代器的hasNextService方法讀取類路徑下META-INF/services目錄的所有名稱為接口全限定名的文件,將其內(nèi)容存入configs對(duì)象中

4.從configs對(duì)象中獲取實(shí)現(xiàn)類的全限定名,然后通過(guò)反射實(shí)例化對(duì)象

從上述流程,我們也可以總結(jié)實(shí)現(xiàn)SPI的幾點(diǎn)重要信息

1.實(shí)現(xiàn)工程必須在類路徑下的META-INF/services目錄下創(chuàng)建接口全限定名的文件,其文件內(nèi)容必須是接口實(shí)現(xiàn)類的全限定名

2.實(shí)現(xiàn)類必須有一個(gè)無(wú)參構(gòu)造方法,因?yàn)镾PI默認(rèn)是使用無(wú)參構(gòu)造方法實(shí)例化對(duì)象的

4.總結(jié)

本文首先概述了Java的SPI機(jī)制,隨后闡述了其基本使用方法,最后深入探討了其實(shí)現(xiàn)原理。SPI在Java語(yǔ)言體系中具有廣泛應(yīng)用,能夠有效地實(shí)現(xiàn)系統(tǒng)解耦,眾多框架基于此機(jī)制進(jìn)行了拓展和優(yōu)化,從而實(shí)現(xiàn)了更為強(qiáng)大的SPI機(jī)制。掌握SPI的使用技巧可以幫助我們?cè)O(shè)計(jì)出更為靈活的系統(tǒng),而深入理解其原理則有助于提升我們的技術(shù)水平。

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2979

    瀏覽量

    105563
  • SPI
    SPI
    +關(guān)注

    關(guān)注

    17

    文章

    1730

    瀏覽量

    92717
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    高保真膽機(jī)制詳解

    http://115.com/file/be3wripk#高保真膽機(jī)制詳解.rar
    發(fā)表于 02-14 09:54

    Java開(kāi)發(fā)利器Myeclipse全面詳解

    Java開(kāi)發(fā)利器Myeclipse全面詳解
    發(fā)表于 11-06 11:17 ?0次下載

    java入門到詳解[推薦]

    java入門到詳解[推薦]
    發(fā)表于 03-19 11:23 ?4次下載

    基于Java反射機(jī)制的Excel文件導(dǎo)出實(shí)現(xiàn)_楊敏煜

    基于Java反射機(jī)制的Excel文件導(dǎo)出實(shí)現(xiàn)_楊敏煜
    發(fā)表于 03-18 09:46 ?1次下載

    java類加載機(jī)制圖文詳解

    解析生成對(duì)應(yīng)的Class對(duì)象,這就是類加載器的功能。我們可以利用類加載器,實(shí)現(xiàn)類的動(dòng)態(tài)加載。 二、類的加載機(jī)制Java中,采用雙親委派機(jī)制來(lái)實(shí)現(xiàn)類的加載。那什么是雙親委派機(jī)制?在
    發(fā)表于 09-27 14:27 ?0次下載
    <b class='flag-5'>java</b>類加載<b class='flag-5'>機(jī)制</b>圖文<b class='flag-5'>詳解</b>

    詳解java并發(fā)機(jī)制

    在一般性開(kāi)發(fā)中,筆者經(jīng)常看到很多同學(xué)在對(duì)待java并發(fā)開(kāi)發(fā)模型中只會(huì)使用一些基礎(chǔ)的方法。比如Volatile,synchronized。像Lock和atomic這類高級(jí)并發(fā)包很多人并不經(jīng)常使用。我想
    發(fā)表于 09-27 14:31 ?0次下載

    java的動(dòng)態(tài)代理機(jī)制和作用

    的我們的功能,我們更需要學(xué)習(xí)的是其底層是怎么樣的一個(gè)原理,而AOP的原理就是java的動(dòng)態(tài)代理機(jī)制,所以本篇隨筆就是對(duì)java的動(dòng)態(tài)機(jī)制進(jìn)行一個(gè)回顧。 在
    發(fā)表于 09-27 14:37 ?0次下載

    java動(dòng)態(tài)代理機(jī)制詳解的類和接口描述

    的我們的功能,我們更需要學(xué)習(xí)的是其底層是怎么樣的一個(gè)原理,而AOP的原理就是java的動(dòng)態(tài)代理機(jī)制,所以本篇隨筆就是對(duì)java的動(dòng)態(tài)機(jī)制進(jìn)行一個(gè)回顧。 在
    發(fā)表于 09-28 13:33 ?0次下載

    Java反射機(jī)制到底是什么?有什么作用

    Java反射機(jī)制Java 語(yǔ)言的一個(gè)重要特性,它在服務(wù)器程序和中間件程序中得到了廣泛運(yùn)用。在服務(wù)器端,往往需要根據(jù)客戶的請(qǐng)求,動(dòng)態(tài)調(diào)用某一個(gè)對(duì)象的特定方法。此外,在 ORM 中間件的實(shí)現(xiàn)中,運(yùn)用
    的頭像 發(fā)表于 02-15 14:07 ?4866次閱讀

    礦石收音機(jī)制詳解

    礦石收音機(jī)制詳解
    發(fā)表于 12-27 17:52 ?63次下載

    源碼級(jí)深度理解Java SPI

    SPI 配置:Java SPI 機(jī)制約定的配置文件,提供查找服務(wù)實(shí)現(xiàn)類的邏輯。配置文件必須置于 META-INF/services 目錄中,并且,文件名應(yīng)與服務(wù)提供者接口的完全限定名保
    的頭像 發(fā)表于 11-15 11:38 ?728次閱讀

    基于spring的SPI擴(kuò)展機(jī)制是如何實(shí)現(xiàn)的?

    基本上,你一說(shuō)是基于 spring 的 SPI 擴(kuò)展機(jī)制,再把spring.factories文件和EnableAutoConfiguration提一下,那么這個(gè)問(wèn)題就答的八九不離十了。
    的頭像 發(fā)表于 03-07 09:17 ?1144次閱讀

    Java、Spring、Dubbo三者SPI機(jī)制的原理和區(qū)別

    其實(shí)我之前寫(xiě)過(guò)一篇類似的文章,但是這篇文章主要是剖析dubbo的SPI機(jī)制的源碼,中間只是簡(jiǎn)單地介紹了一下Java、Spring的SPI機(jī)制
    的頭像 發(fā)表于 06-05 15:21 ?1168次閱讀
    <b class='flag-5'>Java</b>、Spring、Dubbo三者<b class='flag-5'>SPI</b><b class='flag-5'>機(jī)制</b>的原理和區(qū)別

    SPI是什么?Java SPI的使用介紹

    SPI 全稱 Service Provider Interface,是 Java 提供的一套用來(lái)被第三方實(shí)現(xiàn)或者擴(kuò)展的 API,它可以用來(lái)啟用框架擴(kuò)展和替換組件。
    的頭像 發(fā)表于 09-02 09:58 ?1499次閱讀
    <b class='flag-5'>SPI</b>是什么?<b class='flag-5'>Java</b> <b class='flag-5'>SPI</b>的使用介紹

    什么是SPI機(jī)制

    1、前言 在之前的 JVM 分析系列之類加載 提到過(guò) Java SPI 機(jī)制,主要是類加載器反雙親委派的實(shí)現(xiàn)(第三方包不在指定jdk路徑,一般類加載器無(wú)法加載,需要特殊
    的頭像 發(fā)表于 10-08 15:03 ?1284次閱讀
    什么是<b class='flag-5'>SPI</b><b class='flag-5'>機(jī)制</b>
    主站蜘蛛池模板: 五月婷婷婷 | 永久免费的啪啪免费的网址 | 久久国产精品久久久久久 | 亚洲热热久久九九精品 | 亚洲hhh | 97av在线 | 午夜精品福利在线观看 | 一本二卡三卡四卡乱码二百 | 欧美成人性色xxxxx视频大 | 高清欧美一级在线观看 | bt在线www天堂资源网 | 亚洲 欧美 自拍 另类 欧美 | 91九色成人 | 男人午夜免费视频 | 中文字幕精品一区 | 亚洲一区不卡视频 | 日本高清不卡视频 | 国产三级日本三级韩国三级在线观看 | 九九九国产在线 | 久久精品30 | 国产精品午夜寂寞视频 | 99久久综合狠狠综合久久男同 | 亚洲乱亚洲乱妇13p 亚洲免费mv | 成人影院在线观看视频 | 天天射天天射天天干 | 在线观看视频免费 | 久久精品视频7 | 国产精品久久久久久影院 | 天天天色 | 在线观看亚洲天堂 | 无毒不卡在线播放 | 激情综合激情五月 | 激情综合色五月丁香六月亚洲 | 欧美成人天天综合在线视色 | 欧美在线视频看看 | 日本三级免费看 | 狠狠色噜噜狠狠狠狠97不卡 | 国产美女激情视频 | ww欧洲ww在线视频看 | 四虎必出精品亚洲高清 | 亚洲午夜久久 |