1. 程式人生 > 其它 >JAVA的SPI機制

JAVA的SPI機制

SPI詳解,Spring Factories

1.什麼是SPI

SPI全稱Service Provider Interface,是Java提供的一套用來被第三方實現或者擴充套件的介面,它可以用來啟用框架擴充套件和替換元件。SPI的作用就是為這些被擴充套件的API尋找服務實現。

3.SPI的簡單實現

下面我們來簡單實現一個jdk的SPI的簡單實現。

首先第一步,定義一組介面:

1 public interface UploadCDN {
2     void upload(String url);
3 }

這個介面分別有兩個實現:

 1 public class QiyiCDN implements UploadCDN {  //上傳愛奇藝cdn
 2     @Override
 3     public void upload(String url) {
 4         System.out.println("upload to qiyi cdn");
 5     }
 6 }
 7 
 8 public class ChinaNetCDN implements UploadCDN {//上傳網宿cdn
 9     @Override
10     public void upload(String url) {
11         System.out.println("upload to chinaNet cdn");
12     }
13 }

然後需要在resources目錄下新建META-INF/services目錄,並且在這個目錄下新建一個與上述介面的全限定名一致的檔案,在這個檔案中寫入介面的實現類的全限定名:

這時,通過serviceLoader載入實現類並呼叫:

1  public static void main(String[] args) {
2         ServiceLoader<UploadCDN> uploadCDN = ServiceLoader.load(UploadCDN.class);
3         for (UploadCDN u : uploadCDN) {
4             u.upload("filePath");
5         }
6     }

輸出如下:

這樣一個簡單的spi的demo就完成了。可以看到其中最為核心的就是通過ServiceLoader這個類來載入具體的實現類的。

4. SPI原理解析

通過上面簡單的demo,可以看到最關鍵的實現就是ServiceLoader這個類,可以看下這個類的原始碼,如下:

 1 public final class ServiceLoader<S> implements Iterable<S> {
 2 
 3 
 4     //掃描目錄字首
 5     private static final String PREFIX = "META-INF/services/";
 6 
 7     // 被載入的類或介面
 8     private final Class<S> service;
 9 
10     // 用於定位、載入和例項化實現方實現的類的類載入器
11     private final ClassLoader loader;
12 
13     // 上下文物件
14     private final AccessControlContext acc;
15 
16     // 按照例項化的順序快取已經例項化的類
17     private LinkedHashMap<String, S> providers = new LinkedHashMap<>();
18 
19     // 懶查詢迭代器
20     private java.util.ServiceLoader.LazyIterator lookupIterator;
21 
22     // 私有內部類,提供對所有的service的類的載入與例項化
23     private class LazyIterator implements Iterator<S> {
24         Class<S> service;
25         ClassLoader loader;
26         Enumeration<URL> configs = null;
27         String nextName = null;
28 
29         //...
30         private boolean hasNextService() {
31             if (configs == null) {
32                 try {
33                     //獲取目錄下所有的類
34                     String fullName = PREFIX + service.getName();
35                     if (loader == null)
36                         configs = ClassLoader.getSystemResources(fullName);
37                     else
38                         configs = loader.getResources(fullName);
39                 } catch (IOException x) {
40                     //...
41                 }
42                 //....
43             }
44         }
45 
46         private S nextService() {
47             String cn = nextName;
48             nextName = null;
49             Class<?> c = null;
50             try {
51                 //反射載入類
52                 c = Class.forName(cn, false, loader);
53             } catch (ClassNotFoundException x) {
54             }
55             try {
56                 //例項化
57                 S p = service.cast(c.newInstance());
58                 //放進快取
59                 providers.put(cn, p);
60                 return p;
61             } catch (Throwable x) {
62                 //..
63             }
64             //..
65         }
66     }
67 }

上面的程式碼只貼出了部分關鍵的實現,有興趣的讀者可以自己去研究,下面貼出比較直觀的spi載入的主要流程供參考:

Spring Boot的擴充套件機制之Spring Factories

Spring Boot中的SPI機制

在Spring中也有一種類似與Java SPI的載入機制。它在META-INF/spring.factories檔案中配置介面的實現類名稱,然後在程式中讀取這些配置檔案並例項化。
這種自定義的SPI機制是Spring Boot Starter實現的基礎。

spring.factories

Spring Factories實現原理

spring-core包裡定義了SpringFactoriesLoader類,這個類實現了檢索META-INF/spring.factories檔案,並獲取指定介面的配置的功能。在這個類中定義了兩個對外的方法:

loadFactories 根據介面類獲取其實現類的例項,這個方法返回的是物件列表。
loadFactoryNames 根據介面獲取其介面類的名稱,這個方法返回的是類名的列表。
上面的兩個方法的關鍵都是從指定的ClassLoader中獲取spring.factories檔案,並解析得到類名列表,具體程式碼如下

    private static Map<String, List<String>> loadSpringFactories(

從程式碼中我們可以知道,在這個方法中會遍歷整個ClassLoader中所有jar包下的spring.factories檔案。也就是說我們可以在自己的jar中配置spring.factories檔案,不會影響到其它地方的配置,也不會被別人的配置覆蓋。

spring.factories的是通過Properties解析得到的,所以我們在寫檔案中的內容都是安裝下面這種方式配置的:

com.xxx.interface=com.xxx.classname

如果一個介面希望配置多個實現類,可以使用’,’進行分割




參考,SPI詳解Spring Factories