Java之SPI機制


之前開阿里的HSF框架,里面用到了Java的SPI機制,今天閑暇的時候去了解了一下,通過寫博客來記錄一下

 

SPI的全名為Service Provider Interface,我對於該機制的理解是為接口尋找服務實現類。現在公司的系統都是進行了模塊的划分,系統抽象為多個模塊,往往有很多不同的實現方案,比如日志模塊的方案,xml解析模塊、jdbc模塊的方案等。面向的對象的設計里,我們一般推薦模塊之間基於接口編程,模塊之間不對實現類進行硬編碼。一旦代碼里涉及具體的實現類,就違反了可拔插的原則,如果需要替換一種實現,就需要修改代碼。於是就有了SPI這種服務發現機制。

 

java spi的具體使用如下  :

當服務的提供者,提供了服務接口的一種實現之后,在jar包的META-INF/services/目錄里同時創建一個以服務接口命名的文件。該文件里就是實現該服務接口的具體實現類。而當外部程序裝配這個模塊的時候,就能通過該jar包META-INF/services/里的配置文件找到具體的實現類名,並裝載實例化,完成模塊的注入。 

基於這樣一個約定就能很好的找到服務接口的實現類,而不需要再代碼里制定。

jdk提供服務實現查找的一個工具類:java.util.ServiceLoader

 

參考案例:

項目文件結構:

 

參考代碼:

Developer.java

package cn.edu.knowledge.spi;

public interface Developer {

    public String getPrograme();

}

 

 

JavaDeveloper.java

package cn.edu.knowledge.spi;

public class JavaDeveloper implements Developer {

    @Override

    public String getPrograme() {

        return "Java";
    }
}

 

 

META-INF\services文件下的cn.edu.knowledge.spi.Developer文件內容是服務類的全限命名:

cn.edu.knowledge.spi.JavaDeveloper

 

將文件導出為jar包,新建一個項目,在項目中導入該jar,下面的測試類的代碼

Test.java

import java.util.ServiceLoader;
import cn.edu.knowledge.spi.Developer;
public class Test {

    public ServiceLoader<Developer> serviceloader = ServiceLoader.load(Developer.class);

    public static void main(String[] arg) {

        Test devClient = new Test();

        Developer dev = devClient.getDeveloper();

        System.out.println(dev.getPrograme());

    }
    private Developer getDeveloper() {

        Developer lastdev = null;

        for (Developer dev : serviceloader) {

            System.out.println("out." + dev.getPrograme());

            lastdev = dev;

        }
        if(lastdev==null)
            System.out.println("why...");
        return lastdev;

    }

}

 

我們在開發中都有用到SPI機制,但是我們沒有意識到比如:

1.common-logging

apache最早提供的日志的門面接口。只有接口,沒有實現。具體方案由各提供商實現,發現日志提供商是通過掃描  META-INF/services/org.apache.commons.logging.LogFactory 配置文件,通過讀取該文件的內容找到日志提工商實現類。只要我們的日志實現里包含了這個文件,並在文件里制定   LogFactory工廠接口的實現類即可。

2.jdbc

jdbc4.0以前,開發人員還需要基於Class.forName("xxx")的方式來裝載驅動,jdbc4也基於spi的機制來發現驅動提供商了,可以通過META-INF/services/java.sql.Driver文件里指定實現類的方式來暴露驅動提供者。

學習到的知識:面向接口編程可以實現接口和實現的分離,這樣做的最大好處就是能夠在客戶端未知的情況下修改實現代碼。那么什么時候應該抽象出Java接口呢?一種是用在層和層之問的調用。層和層之間是最忌諱耦合度過高或是改變過於頻繁。設計優秀的接口能夠解決這個問題。另一種是用在那些不穩定的部分上。如果某些需求的變化性很大,那么定義接口也是一種解決之道。設計良好的接口就像是我們日常使用的萬用插座一樣,不論插頭如何變化,都可以使用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM