最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入探討Java?SPI機(jī)制及其應(yīng)用場(chǎng)景

 更新時(shí)間:2023年04月26日 10:17:40   作者:頑石九變  
本文深入介紹了Java?SPI機(jī)制的原理和實(shí)現(xiàn)方式,以及在Java中如何使用SPI實(shí)現(xiàn)可插拔的組件化架構(gòu),包括示例代碼和應(yīng)用場(chǎng)景分析,幫助讀者更好地理解和應(yīng)用該機(jī)制

一、什么是SPI

SPI全稱Service Provider Interface,是Java提供的一種服務(wù)發(fā)現(xiàn)機(jī)制。實(shí)現(xiàn)服務(wù)接口和服務(wù)實(shí)現(xiàn)的解耦。

Java SPI 實(shí)際上是“基于接口的編程+策略模式+配置文件”組合實(shí)現(xiàn)的動(dòng)態(tài)加載機(jī)制,實(shí)現(xiàn)不修改任何代碼的情況下切換不同的實(shí)現(xiàn)。

二、使用場(chǎng)景

很多開源第三方j(luò)ar包都有基于SPI的實(shí)現(xiàn),在jar包META-INF/services中都有相關(guān)配置文件。

如下幾個(gè)常見(jiàn)的場(chǎng)景:

1)JDBC加載不同類型的數(shù)據(jù)庫(kù)驅(qū)動(dòng)

2)Slf4j日志框架

3)Dubbo框架

三、使用步驟示例

假設(shè)有個(gè)上傳附件的場(chǎng)景,可以上傳到不同的云儲(chǔ)存(如阿里云OSS,亞馬遜S3),那么基于Java SPI機(jī)制的實(shí)現(xiàn),我們應(yīng)該做如下步驟:

步驟1、創(chuàng)建4個(gè)工程

SPI的核心就是實(shí)現(xiàn)服務(wù)接口和服務(wù)實(shí)現(xiàn)的解耦,所以我們不能將接口和實(shí)現(xiàn)放在一個(gè)工程里面。

  • spi-file-upload,在這定義附件上傳接口IFileUpload
  • spi-file-upload-oss,實(shí)現(xiàn)附件上傳到oss,FileUploadOss實(shí)現(xiàn)接口IFileUpload
  • spi-file-upload-s3,實(shí)現(xiàn)附件上傳到s3,FileUploadS3實(shí)現(xiàn)接口IFileUpload
  • spi-file-upload-test,通過(guò)ServiceLoader加載接口實(shí)現(xiàn),進(jìn)行測(cè)試

步驟2、 在工程spi-file-upload創(chuàng)建接口IFileUpload

接口代碼示例

package com.hj.test.file.oss;
/**
 * 文件上傳接口
 */
public interface IFileUpload {
    void upload(String fileName);
}

步驟3、分別創(chuàng)建接口實(shí)現(xiàn)類FileUploadOss、FileUploadS3

1)FileUploadOss

在工程的 spi-file-upload-oss 的 resources目錄下創(chuàng)建目錄META-INF/services,并在該目錄中創(chuàng)建以接口IFileUpload全路徑命名的文件(com.hj.test.file.IFileUpload),文件內(nèi)容是接口實(shí)現(xiàn)類 com.hj.test.file.oss.FileUploadOss

package com.hj.test.file.oss;
import com.hj.test.file.IFileUpload;
public class FileUploadOss implements IFileUpload {
    @Override
    public void upload(String fileName) {
        System.out.println("上傳到阿里云OSS..." + fileName);
    }
}

2)FileUploadS3

在工程的 spi-file-upload-s3 的 resources目錄下創(chuàng)建目錄META-INF/services,并在該目錄中創(chuàng)建以接口IFileUpload全路徑命名的文件(com.hj.test.file.IFileUpload),文件內(nèi)容是接口實(shí)現(xiàn)類 com.hj.test.file.s3.FileUploadS3

package com.hj.test.file.s3;
import com.hj.test.file.IFileUpload;
public class FileUploadS3 implements IFileUpload {
    @Override
    public void upload(String fileName) {
        System.out.println("上傳到亞馬遜s3..." + fileName);
    }
}

步驟4、在工程spi-file-upload-test中創(chuàng)建測(cè)試調(diào)用類

1)在pom.xml中引入3個(gè)依賴工程

<dependency>
    <groupId>com.hj</groupId>
    <artifactId>spi-file-upload</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>com.hj</groupId>
    <artifactId>spi-file-upload-oss</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>com.hj</groupId>
    <artifactId>spi-file-upload-s3</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

2)測(cè)試實(shí)現(xiàn)

package com.hj.test.file.test;
import com.hj.test.file.IFileUpload;
import java.util.Iterator;
import java.util.ServiceLoader;
public class FileTest{
    public static void main(String[] args) {
        ServiceLoader<IFileUpload> loader = ServiceLoader.load(IFileUpload.class);
        for(Iterator<IFileUpload> it = loader.iterator(); it.hasNext();){
            IFileUpload file = it.next();
            file.upload("測(cè)試文件上傳");
        }
    }
}

控制臺(tái)輸出

上傳到阿里云OSS...測(cè)試文件上傳
上傳到亞馬遜s3...測(cè)試文件上傳

如果哪天不想要傳到s3,只需要把jar包依賴去掉就可以,無(wú)需改代碼

四、原理解析

1、SPI的核心就是ServiceLoader.load()方法

總結(jié)如下:

  1. 調(diào)用ServiceLoader.load(),創(chuàng)建一個(gè)ServiceLoader實(shí)例對(duì)象
  2. 創(chuàng)建LazyIterator實(shí)例對(duì)象lookupIterator
  3. 通過(guò)lookupIterator.hasNextService()方法讀取固定目錄META-INF/services/下面service全限定名文件,放在Enumeration對(duì)象configs
  4. 解析configs得到迭代器對(duì)象Iterator<String> pending
  5. 通過(guò)lookupIterator.nextService()方法初始化讀取到的實(shí)現(xiàn)類,通過(guò)Class.forName()初始化

從上面的步驟可以總結(jié)以下幾點(diǎn)

  • 實(shí)現(xiàn)類工程必須創(chuàng)建定目錄META-INF/services/,并創(chuàng)建service全限定名文件,文件內(nèi)容是實(shí)現(xiàn)類全限定名
  • 實(shí)現(xiàn)類必須有一個(gè)無(wú)參構(gòu)造函數(shù)

2、ServiceLoader核心代碼介紹

public final class ServiceLoader<S>
    implements Iterable<S>
{
    private static final String PREFIX = "META-INF/services/";
    // The class or interface representing the service being loaded
    private final Class<S> 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<String,S> providers = new LinkedHashMap<>();
    // The current lazy-lookup iterator
    private LazyIterator lookupIterator;
public static <S> ServiceLoader<S> load(Class<S> service,
                                        ClassLoader loader)
{
    return new ServiceLoader<>(service, loader);
}
public void reload() {
    providers.clear();
    lookupIterator = new LazyIterator(service, loader);
}
private ServiceLoader(Class<S> svc, ClassLoader cl) {
    service = Objects.requireNonNull(svc, "Service interface cannot be null");
    loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
    acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
    reload();
}

通過(guò)方法iterator()生成迭代器,內(nèi)部調(diào)用LazyIterator實(shí)例對(duì)象

public Iterator<S> iterator() {
    return new Iterator<S>() {
        Iterator<Map.Entry<String,S>> 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();
        }
    };
}

內(nèi)部類LazyIterator,讀取配置文件META-INF/services/

private class LazyIterator
        implements 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 {
                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;
    }
    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
    }
    public boolean hasNext() {
        if (acc == null) {
            return hasNextService();
        } else {
            PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
                public Boolean run() { return hasNextService(); }
            };
            return AccessController.doPrivileged(action, acc);
        }
    }
    public S next() {
        if (acc == null) {
            return nextService();
        } else {
            PrivilegedAction<S> action = new PrivilegedAction<S>() {
                public S run() { return nextService(); }
            };
            return AccessController.doPrivileged(action, acc);
        }
    }
    public void remove() {
        throw new UnsupportedOperationException();
    }
}

到此這篇關(guān)于深入探討Java SPI機(jī)制及其應(yīng)用場(chǎng)景的文章就介紹到這了,更多相關(guān)Java SPI機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • mybatis中的if?test判斷入?yún)⒌闹祮?wèn)題

    mybatis中的if?test判斷入?yún)⒌闹祮?wèn)題

    這篇文章主要介紹了mybatis中的if?test判斷入?yún)⒌闹祮?wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • 如何解決@SpringBootApplication報(bào)錯(cuò)問(wèn)題

    如何解決@SpringBootApplication報(bào)錯(cuò)問(wèn)題

    這篇文章主要介紹了如何解決@SpringBootApplication報(bào)錯(cuò)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Spring Data Jpa實(shí)現(xiàn)分頁(yè)和排序代碼實(shí)例

    Spring Data Jpa實(shí)現(xiàn)分頁(yè)和排序代碼實(shí)例

    本篇文章主要介紹了Spring Data Jpa實(shí)現(xiàn)分頁(yè)和排序代碼實(shí)例,具有一定的參考價(jià)值,有興趣的可以了解一下。
    2017-03-03
  • 計(jì)算Java數(shù)組長(zhǎng)度函數(shù)的方法以及代碼分析

    計(jì)算Java數(shù)組長(zhǎng)度函數(shù)的方法以及代碼分析

    在本篇內(nèi)容里,小編給大家整理了關(guān)于計(jì)算Java數(shù)組長(zhǎng)度函數(shù)的方法以及代碼分析內(nèi)容,有興趣的朋友么可以學(xué)習(xí)參考下。
    2022-11-11
  • 解讀java.lang.Character.isLetterOrDigit()的使用方式

    解讀java.lang.Character.isLetterOrDigit()的使用方式

    這篇文章主要介紹了解讀java.lang.Character.isLetterOrDigit()的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • Java中ArrayList集合的常用方法大全

    Java中ArrayList集合的常用方法大全

    這篇文章主要給大家介紹了關(guān)于Java中ArrayList集合的常用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • MyBatis-Flex BaseMapper的接口基本用法小結(jié)

    MyBatis-Flex BaseMapper的接口基本用法小結(jié)

    本文主要介紹了MyBatis-Flex BaseMapper的接口基本用法小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-02-02
  • 圖文詳解OkHttp的超時(shí)時(shí)間

    圖文詳解OkHttp的超時(shí)時(shí)間

    HTTP是現(xiàn)代應(yīng)用常用的一種交換數(shù)據(jù)和媒體的網(wǎng)絡(luò)方式,高效地使用HTTP能讓資源加載更快,節(jié)省帶寬,OkHttp是一個(gè)高效的HTTP客戶端,下面這篇文章主要給大家介紹了關(guān)于OkHttp超時(shí)時(shí)間的相關(guān)資料,需要的朋友可以參考下
    2021-10-10
  • 教你在 IntelliJ IDEA 中使用 VIM插件的詳細(xì)教程

    教你在 IntelliJ IDEA 中使用 VIM插件的詳細(xì)教程

    這篇文章主要介紹了在 IntelliJ IDEA 中使用 VIM的教程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-05-05
  • Spring Security驗(yàn)證流程剖析及自定義驗(yàn)證方法

    Spring Security驗(yàn)證流程剖析及自定義驗(yàn)證方法

    Spring Security是一個(gè)能夠?yàn)榛赟pring的企業(yè)應(yīng)用系統(tǒng)提供聲明式的安全訪問(wèn)控制解決方案的安全框架。這篇文章主要介紹了Spring Security驗(yàn)證流程剖析及自定義驗(yàn)證方法,需要的朋友可以參考下
    2018-03-03

最新評(píng)論

通化市| 榆树市| 萍乡市| 九龙县| 镶黄旗| 雷山县| 闸北区| 冷水江市| 西贡区| 卢湾区| 繁昌县| 新丰县| 庐江县| 阳山县| 兴宁市| 海口市| 平乐县| 江门市| 新绛县| 永清县| 三河市| 永宁县| 巧家县| 东兴市| 焉耆| 武强县| 彭水| 汉阴县| 崇左市| 台东市| 祁阳县| 无锡市| 紫阳县| 郓城县| 安国市| 綦江县| 漾濞| 盘山县| 兰考县| 东兰县| 石棉县|