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

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

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

一、什么是SPI

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

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

二、使用場景

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

如下幾個常見的場景:

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

2)Slf4j日志框架

3)Dubbo框架

三、使用步驟示例

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

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

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

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

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

接口代碼示例

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

步驟3、分別創(chuàng)建接口實現(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)容是接口實現(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)容是接口實現(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)建測試調(diào)用類

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

<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)測試實現(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("測試文件上傳");
        }
    }
}

控制臺輸出

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

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

四、原理解析

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

總結(jié)如下:

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

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

  • 實現(xiàn)類工程必須創(chuàng)建定目錄META-INF/services/,并創(chuàng)建service全限定名文件,文件內(nèi)容是實現(xiàn)類全限定名
  • 實現(xiàn)類必須有一個無參構(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();
}

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

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機制及其應(yīng)用場景的文章就介紹到這了,更多相關(guān)Java SPI機制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • mybatis中的if?test判斷入?yún)⒌闹祮栴}

    mybatis中的if?test判斷入?yún)⒌闹祮栴}

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

    如何解決@SpringBootApplication報錯問題

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

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

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

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

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

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

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

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

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

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

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

    圖文詳解OkHttp的超時時間

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

    教你在 IntelliJ IDEA 中使用 VIM插件的詳細教程

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

    Spring Security驗證流程剖析及自定義驗證方法

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

最新評論

汉阴县| 阿巴嘎旗| 通城县| 台北市| 怀远县| 久治县| 九龙坡区| 柳江县| 中宁县| 青浦区| 敖汉旗| 远安县| 正宁县| 大余县| 石家庄市| 九龙城区| 常熟市| 阿尔山市| 双江| 那坡县| 汤原县| 刚察县| 广平县| 大方县| 九龙城区| 金山区| 静宁县| 双流县| 沈阳市| 安宁市| 肥城市| 杂多县| 德化县| 河北省| 星子县| 曲水县| 札达县| 准格尔旗| 罗定市| 内乡县| 天门市|