中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java?SPI機制及其應用場景是什么

發布時間:2023-04-26 11:55:00 來源:億速云 閱讀:134 作者:iii 欄目:開發技術

今天小編給大家分享一下Java SPI機制及其應用場景是什么的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

一、什么是SPI

SPI全稱Service Provider Interface,是Java提供的一種服務發現機制。實現服務接口和服務實現的解耦。

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

Java?SPI機制及其應用場景是什么

二、使用場景

很多開源第三方jar包都有基于SPI的實現,在jar包META-INF/services中都有相關配置文件。

如下幾個常見的場景:

1)JDBC加載不同類型的數據庫驅動

2)Slf4j日志框架

3)Dubbo框架

三、使用步驟示例

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

步驟1、創建4個工程

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

  • spi-file-upload,在這定義附件上傳接口IFileUpload

  • spi-file-upload-oss,實現附件上傳到oss,FileUploadOss實現接口IFileUpload

  • spi-file-upload-s3,實現附件上傳到s3,FileUploadS3實現接口IFileUpload

  • spi-file-upload-test,通過ServiceLoader加載接口實現,進行測試

Java?SPI機制及其應用場景是什么

步驟2、 在工程spi-file-upload創建接口IFileUpload

接口代碼示例

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

步驟3、分別創建接口實現類FileUploadOss、FileUploadS3

1)FileUploadOss

在工程的 spi-file-upload-oss 的 resources目錄下創建目錄META-INF/services,并在該目錄中創建以接口IFileUpload全路徑命名的文件(com.hj.test.file.IFileUpload),文件內容是接口實現類 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);
    }
}

Java?SPI機制及其應用場景是什么

2)FileUploadS3

在工程的 spi-file-upload-s3 的 resources目錄下創建目錄META-INF/services,并在該目錄中創建以接口IFileUpload全路徑命名的文件(com.hj.test.file.IFileUpload),文件內容是接口實現類 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中創建測試調用類

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)測試實現

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()方法

總結如下:

  1. 調用ServiceLoader.load(),創建一個ServiceLoader實例對象

  2. 創建LazyIterator實例對象lookupIterator

  3. 通過lookupIterator.hasNextService()方法讀取固定目錄META-INF/services/下面service全限定名文件,放在Enumeration對象configs

  4. 解析configs得到迭代器對象Iterator<String> pending

  5. 通過lookupIterator.nextService()方法初始化讀取到的實現類,通過Class.forName()初始化

從上面的步驟可以總結以下幾點

  • 實現類工程必須創建定目錄META-INF/services/,并創建service全限定名文件,文件內容是實現類全限定名

  • 實現類必須有一個無參構造函數

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()生成迭代器,內部調用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();
        }
    };
}

內部類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();
    }
}

以上就是“Java SPI機制及其應用場景是什么”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

隆尧县| 随州市| 临猗县| 汝阳县| 天等县| 长泰县| 江源县| 偏关县| 揭西县| 衡东县| 鄢陵县| 双鸭山市| 察哈| 杭锦旗| 界首市| 得荣县| 澄城县| 上犹县| 湘潭市| 广东省| 仙游县| 嘉兴市| 苍南县| 苏尼特右旗| 方正县| 且末县| 黄平县| 罗山县| 鞍山市| 安顺市| 鸡东县| 陇南市| 涟水县| 元阳县| 柳河县| 施秉县| 海兴县| 丹江口市| 周至县| 万安县| 云梦县|