亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

一文帶你了解Java中的SPI機制

 更新時間:2023年04月09日 08:51:58   作者:笨笨的二黃子  
SPI 全稱是 Service Provider Interface,是一種 JDK 內置的動態(tài)加載實現(xiàn)擴展點的機制,本文主要為大家介紹了SPI機制的原理與使用,需要的可以參考一下

1: SPI機制簡介

SPI 全稱是 Service Provider Interface,是一種 JDK 內置的動態(tài)加載實現(xiàn)擴展點的機制,通過 SPI 技術我們可以動態(tài)獲取接口的實現(xiàn)類,不用自己來創(chuàng)建。這個不是什么特別的技術,只是 一種設計理念。

2: SPI原理

Java SPI 實際上是基于接口的編程+策略模式+配置文件組合實現(xiàn)的動態(tài)加載機制。

系統(tǒng)設計的各個抽象,往往有很多不同的實現(xiàn)方案,在面向的對象的設計里,一般推薦模塊之間基于接口編程,模塊之間不對實現(xiàn)類進行硬編碼。一旦代碼里涉及具體的實現(xiàn)類,就違反了可拔插的原則,如果需要替換一種實現(xiàn),就需要修改代碼。為了實現(xiàn)在模塊裝配的時候能不在程序里動態(tài)指明,這就需要一種服務發(fā)現(xiàn)機制。

Java SPI就是提供這樣的一個機制:為某個接口尋找服務實現(xiàn)的機制。有點類似IOC的思想,就是將裝配的控制權移到程序之外,在模塊化設計中這個機制尤其重要。所以SPI的核心思想就是解耦。

3: 使用場景

調用者根據(jù)實際使用需要 啟用、擴展、或者替換框架的實現(xiàn)策略

下面是一些使用了該機制的場景

  • JDBC驅動,加載不同數(shù)據(jù)庫的驅動類
  • Spring中大量使用了SPI,比如:對servlet3.0規(guī)范對ServletContainerInitializer的實現(xiàn)、自動類型轉換Type Conversion SPI(Converter SPI、Formatter SPI)等
  • Dubbo中也大量使用SPI的方式實現(xiàn)框架的擴展, 不過它對Java提供的原生SPI做了封裝,允許用戶擴展實現(xiàn)Filter接口
  • Tomcat 加載 META-INF/services下找需要加載的類
  • SpringBoot項目中 使用@SpringBootApplication注解時,會開始自動配置,而啟動配置則會去掃描META-INF/spring.factories下的配置類

4: 源碼論證

4.1 應用程序調用ServiceLoader.load方法

ServiceLoader.load方法內先創(chuàng)建一個新的ServiceLoader,并實例化該類中的成員變量

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


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

	/** 
     * 
     * 在調用該方法之后,迭代器方法的后續(xù)調用將延遲地從頭開始查找和實例化提供程序,就像新創(chuàng)建的加載程序所做的		  那樣
     */
   public void reload() {
        providers.clear(); //清除此加載程序的提供程序緩存,以便重新加載所有提供程序。
        lookupIterator = new LazyIterator(service, loader);
    }

	private class LazyIterator implements Iterator<S>{

        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        Iterator<String> pending = null;
        String nextName = null;


        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;
        }
    }

		/** 
     	* 
     	*  通過反射 實例化配置文件中的具體實現(xiàn)類
    	 */
		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
        }

5: 實戰(zhàn)

步驟1 新建以下類

public interface IService {

    /**
     * 獲取價格
     * @return
     */
    String getPrice();

    /**
     * 獲取規(guī)格信息
     * @return
     */
    String getSpecifications();
}
public class GoodServiceImpl implements IService {

    @Override
    public String getPrice() {
        return "2000.00元";
    }

    @Override
    public String getSpecifications() {
        return "200g/件";
    }
}
public class MedicalServiceImpl implements IService {

    @Override
    public String getPrice() {
        return "3022.12元";
    }

    @Override
    public String getSpecifications() {
        return "30粒/盒";
    }
}

步驟2、在 src/main/resources/ 下建立 /META-INF/services 目錄, 新增一個以接口命名的文件 org.example.IService.txt 。內容是要應用的實現(xiàn)類,我這邊需要放入的數(shù)據(jù)如下

org.example.GoodServiceImpl
org.example.MedicalServiceImpl

步驟3、使用 ServiceLoader 來加載配置文件中指定的實現(xiàn)。

public class Main {
    public static void main(String[] args) {
        final ServiceLoader<IService> serviceLoader = ServiceLoader.load(IService.class);
        serviceLoader.forEach(service -> {
            System.out.println(service.getPrice() + "=" + service.getSpecifications());
        });
    }
}

輸出:

2000.00元=200g/件
3022.12元=30粒/盒

6: 優(yōu)缺點

6.1 優(yōu)點

解耦 使得第三方服務模塊的裝配控制的邏輯與調用者的業(yè)務代碼分離,而不是耦合在一起,應用程序可以根據(jù)實際業(yè)務情況啟用框架擴展或替換框架組件。相比使用提供接口jar包,供第三方服務模塊實現(xiàn)接口的方式,SPI的方式使得源框架,不必關心接口的實現(xiàn)類的路徑

6.2 缺點

  • 雖然ServiceLoader也算是使用的延遲加載,但是基本只能通過遍歷全部獲取,也就是接口的實現(xiàn)類全部加載并實例化一遍。如果你并不想用某些實現(xiàn)類,它也被加載并實例化了,這就造成了浪費。獲取某個實現(xiàn)類的方式不夠靈活,只能通過Iterator形式獲取,不能根據(jù)某個參數(shù)來獲取對應的實現(xiàn)類
  • 多個并發(fā)多線程使用ServiceLoader類的實例是不安全的

到此這篇關于一文帶你了解Java中的SPI機制的文章就介紹到這了,更多相關Java SPI機制內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java 中校驗時間格式的常見方法

    Java 中校驗時間格式的常見方法

    在實際項目開發(fā)中,跟時間參數(shù)打交道是必不可少的,為了保證程序的安全性、健壯性,一般都會對參數(shù)進行校驗,其他類型的參數(shù)校驗很好實現(xiàn),那你知道時間參數(shù)的是怎么校驗的嗎,下面給大家分享Java 中校驗時間格式的方法,感興趣的朋友跟隨小編一起看看吧
    2024-08-08
  • Redis內存數(shù)據(jù)庫示例分析

    Redis內存數(shù)據(jù)庫示例分析

    Redis本身的內容比較復雜。如果你上來,你應該研究一個細節(jié)點,比如連接池和數(shù)據(jù)結構。雖然可以直接了解某一點的詳細來源內容,甚至盡快解決一些意外,但是容易淹沒在失眠的細節(jié)中,整體控制不了Redis
    2022-12-12
  • Java中上傳圖片壓縮處理的方法示例

    Java中上傳圖片壓縮處理的方法示例

    本篇文章主要介紹了Java中圖片壓縮處理的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-02-02
  • idea中maven項目模塊變成灰色原因及解決方案

    idea中maven項目模塊變成灰色原因及解決方案

    這篇文章主要介紹了idea中maven項目模塊變成灰色原因及解決方案,文中通過圖文結合的方式給大家講解的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下
    2024-03-03
  • Spring Security攔截器引起Java CORS跨域失敗的問題及解決

    Spring Security攔截器引起Java CORS跨域失敗的問題及解決

    這篇文章主要介紹了Spring Security攔截器引起Java CORS跨域失敗的問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • springboot代碼,注解配置獲取yml,properties文件的map即鍵值對

    springboot代碼,注解配置獲取yml,properties文件的map即鍵值對

    這篇文章主要介紹了springboot代碼,注解配置獲取yml,properties文件的map即鍵值對,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Java判斷用戶輸入月份的季節(jié)

    Java判斷用戶輸入月份的季節(jié)

    這篇文章主要為大家詳細介紹了Java判斷用戶輸入月份的季節(jié),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • java獲取日期的方法

    java獲取日期的方法

    這篇文章介紹了java獲取日期的方法,有需要的朋友可以參考一下
    2013-10-10
  • 淺談java中靜態(tài)方法的重寫問題詳解

    淺談java中靜態(tài)方法的重寫問題詳解

    本篇文章是對java中靜態(tài)方法的重寫問題進行了詳細的分析介紹,需要的朋友參考下
    2013-06-06
  • java編程scanner類用法示例

    java編程scanner類用法示例

    這篇文章主要介紹了java編程scanner類用法示例,涉及一個通過scanner類實現(xiàn)需要手動輸入變量時進行輸入的實例,然后分享了一個簡單的eclipse對Java代碼格式化的技巧,具有一定借鑒價值,需要的朋友可以參考。
    2017-11-11

最新評論