jdk SPI與dubbo SPI

1.jdk SPI介紹


SPI 全稱為 Service Provider Interface,是一種服務(wù)發(fā)現(xiàn)機(jī)制。SPI 的\color{red}{本質(zhì)是將接口實(shí)現(xiàn)類的全限定名配置在文件中,并由服務(wù)加載器讀取配置文件,加載實(shí)現(xiàn)類。}這樣可以在\color{red}{運(yùn)行時(shí),動(dòng)態(tài)為接口替換實(shí)現(xiàn)類。正因此特性},我們可以很容易的通過(guò) SPI 機(jī)制為我們的程序提供拓展功能。SPI 機(jī)制在第三方框架中也有所應(yīng)用,比如 Dubbo 就是通過(guò) SPI 機(jī)制加載所有的組件。不過(guò),Dubbo 并未使用 Java 原生的 SPI 機(jī)制,而是對(duì)其進(jìn)行了增強(qiáng),使其能夠更好的滿足需求。在 Dubbo 中,SPI 是一個(gè)非常重要的模塊。基于 SPI,我們可以很容易的對(duì) Dubbo 進(jìn)行拓展。如果大家想要學(xué)習(xí) Dubbo 的源碼,SPI 機(jī)制務(wù)必弄懂。接下來(lái),我們先來(lái)了解一下 Java SPI 與 Dubbo SPI 的用法,然后再來(lái)分析 Dubbo SPI 的源碼。以上來(lái)自dubbo官方文檔

我們系統(tǒng)里抽象的各個(gè)模塊,往往有很多不同的實(shí)現(xiàn)方案,比如日志模塊的方案,xml解析模塊、jdbc模塊的方案等。面向的對(duì)象的設(shè)計(jì)里,我們一般推薦模塊之間基于接口編程,模塊之間不對(duì)實(shí)現(xiàn)類進(jìn)行硬編碼。一旦代碼里涉及具體的實(shí)現(xiàn)類,就違反了可拔插的原則,如果需要替換一種實(shí)現(xiàn),就需要修改代碼。為了實(shí)現(xiàn)在模塊裝配的時(shí)候能不在程序里動(dòng)態(tài)指明,這就需要一種服務(wù)發(fā)現(xiàn)機(jī)制。 java spi就是提供這樣的一個(gè)機(jī)制:為某個(gè)接口尋找服務(wù)實(shí)現(xiàn)的機(jī)制。有點(diǎn)類似IOC的思想,就是將裝配的控制權(quán)移到程序之外,在模塊化設(shè)計(jì)中這個(gè)機(jī)制尤其重要。

SPI的具體約定:服務(wù)的提供者,提供了服務(wù)接口的一種實(shí)現(xiàn)之后,在jar包的META-INF/services/目錄里同時(shí)創(chuàng)建一個(gè)以服務(wù)接口命名的文件。該文件里就是實(shí)現(xiàn)該服務(wù)接口的具體實(shí)現(xiàn)類。而當(dāng)外部程序裝配這個(gè)模塊的時(shí)候,就能通過(guò)該jar包META-INF/services/里的配置文件找到具體的實(shí)現(xiàn)類名,并裝載實(shí)例化,完成模塊的注入。 基于這樣一個(gè)約定就能很好的找到服務(wù)接口的實(shí)現(xiàn)類,而不需要再代碼里制定。jdk提供服務(wù)實(shí)現(xiàn)查找的一個(gè)工具類:java.util.ServiceLoader

  • common-logging

apache最早提供的日志的門面接口。只有接口,沒(méi)有實(shí)現(xiàn)。具體方案由各提供商實(shí)現(xiàn), 發(fā)現(xiàn)日志提供商是通過(guò)掃描 META-INF/services/org.apache.commons.logging.LogFactory配置文件,通過(guò)讀取該文件的內(nèi)容找到日志提工商實(shí)現(xiàn)類。只要我們的日志實(shí)現(xiàn)里包含了這個(gè)文件,并在文件里制定 LogFactory工廠接口的實(shí)現(xiàn)類即可。

  • JDBC

jdbc4.0以前, 開(kāi)發(fā)人員還需要基于Class.forName("xxx")的方式來(lái)裝載驅(qū)動(dòng),jdbc4也基于spi的機(jī)制來(lái)發(fā)現(xiàn)驅(qū)動(dòng)提供商了,可以通過(guò)META-INF/services/java.sql.Driver文件里指定實(shí)現(xiàn)類的方式來(lái)暴露驅(qū)動(dòng)提供者.

jdbc連接過(guò)程 賈璉預(yù)執(zhí)事(加載驅(qū)動(dòng)、鏈接數(shù)據(jù)庫(kù)、預(yù)執(zhí)行、執(zhí)行、釋放資源)

//加載JDBC驅(qū)動(dòng)程序
Class.forName("com.mysql.jdbc.Driver") ; 
//2、提供JDBC連接的URL
String url = jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8
//3、創(chuàng)建數(shù)據(jù)庫(kù)的連接
Connection con =  DriverManager.getConnection(url , username , password ) ; 
//4、創(chuàng)建一個(gè)Statement
PreparedStatement pstmt = con.prepareStatement(sql) ;   
//5、執(zhí)行SQL語(yǔ)句
ResultSet rs = stmt.executeQuery("SELECT * FROM ...") ;   
 while(rs.next()){   
         //do something
     }  
//6、釋放資源
con.close()

我們都知道,也聽(tīng)了無(wú)數(shù)遍,驅(qū)動(dòng)的加載是由Class.forName 方法完成的。
由于JVM對(duì)類的加載有一個(gè)邏輯是:在類被需要的時(shí)候,或者首次調(diào)用的時(shí)候就會(huì)把類加載到JVM。反過(guò)來(lái)也就是:如果類沒(méi)有被需要的時(shí)候,一般是不會(huì)被加載到JVM的。

當(dāng)連接數(shù)據(jù)庫(kù)的時(shí)候我們調(diào)用了Class.forName語(yǔ)句之后,數(shù)據(jù)庫(kù)驅(qū)動(dòng)類被加載到JVM,那么靜態(tài)初始化塊就會(huì)被執(zhí)行,從而完成驅(qū)動(dòng)的注冊(cè)工作,也就是注冊(cè)到了JDBC的DriverManager類中。

由于是靜態(tài)初始化塊中完成的加載,所以也就不必?fù)?dān)心驅(qū)動(dòng)被加載多次,原因可以參考單例模式相關(guān)的知識(shí)。

拋棄Class.forName()

在JDBC 4.0之后實(shí)際上我們不需要再調(diào)用Class.forName來(lái)加載驅(qū)動(dòng)程序了,我們只需要把驅(qū)動(dòng)的jar包放到工程的類加載路徑里,那么驅(qū)動(dòng)就會(huì)被自動(dòng)加載。

這個(gè)自動(dòng)加載采用的技術(shù)叫做SPI,數(shù)據(jù)庫(kù)驅(qū)動(dòng)廠商也都做了更新??梢钥匆幌耲ar包里面的META-INF/services目錄,里面有一個(gè)java.sql.Driver的文件,文件里面包含了驅(qū)動(dòng)的全路徑名。

比如mysql-connector里面的內(nèi)容:

com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver

那么SPI技術(shù)又是在什么階段加載的數(shù)據(jù)庫(kù)驅(qū)動(dòng)呢?看一下JDBC的DriverManager類就知道了。

public class DriverManager {
    static {
        loadInitialDrivers();//......1
        println("JDBC DriverManager initialized");
    }

    private static void loadInitialDrivers() {
        String drivers;
        try {
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                 return System.getProperty("jdbc.drivers");
                }
                });
           } catch (Exception ex) {
                drivers = null;
           }

           AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);//.....2
                   Iterator driversIterator = loadedDrivers.iterator();

                //.....
}

上述代碼片段標(biāo)記…1的位置是在DriverManager類加載是執(zhí)行的靜態(tài)初始化塊,這里會(huì)調(diào)用loadInitialDrivers方法。

再看loadInitialDrivers方法里面標(biāo)記…2的位置,這里調(diào)用的 ServiceLoader.load(Driver.class); 就會(huì)加載所有在META-INF/services/java.sql.Driver文件里邊的類到JVM內(nèi)存,完成驅(qū)動(dòng)的自動(dòng)加載。

這就是SPI的優(yōu)勢(shì)所在,能夠自動(dòng)的加載類到JVM內(nèi)存。這個(gè)技術(shù)在阿里的dubbo框架里面也占到了很大的分量,有興趣的朋友可以看一下dubbo的代碼,或者百度一下dubbo的擴(kuò)展機(jī)制。

JDBC如何區(qū)分多個(gè)驅(qū)動(dòng)?

一個(gè)項(xiàng)目里邊很可能會(huì)即連接MySQL,又連接Oracle,這樣在一個(gè)工程里邊就存在了多個(gè)驅(qū)動(dòng)類,那么這些驅(qū)動(dòng)類又是怎么區(qū)分的呢?

關(guān)鍵點(diǎn)就在于getConnection的步驟,DriverManager.getConnection中會(huì)遍歷所有已經(jīng)加載的驅(qū)動(dòng)實(shí)例去創(chuàng)建連接,當(dāng)一個(gè)驅(qū)動(dòng)創(chuàng)建連接成功時(shí)就會(huì)返回這個(gè)連接,同時(shí)不再調(diào)用其他的驅(qū)動(dòng)實(shí)例。DriverManager關(guān)鍵代碼如下:

private static Connection getConnection(
    //.....

    for(DriverInfo aDriver : registeredDrivers) {
        if(isDriverAllowed(aDriver.driver, callerCL)) {
            try {
                println("    trying " + aDriver.driver.getClass().getName());
                Connection con = aDriver.driver.connect(url, info);
                if (con != null) {
                    // Success!
                    println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                }
            } catch (SQLException ex) {
                if (reason == null) {
                      reason = ex;
                }
            }
        } else {
            println("    skipping: " + aDriver.getClass().getName());
        }
     }

是不是每個(gè)驅(qū)動(dòng)實(shí)例都真真實(shí)實(shí)的要嘗試建立連接呢?不是的!

\color{red}{每個(gè)驅(qū)動(dòng)實(shí)例在getConnetion的第一步就是按照url判斷是不是符合自己的處理規(guī)則,是的話才會(huì)和db建立連接。}比如,MySQL驅(qū)動(dòng)類中的關(guān)鍵代碼:

public boolean acceptsURL(String url) throws SQLException {
        return (parseURL(url, null) != null);
    }

    public Properties parseURL(String url, Properties defaults)
            throws java.sql.SQLException {
        Properties urlProps = (defaults != null) ? new Properties(defaults)
                : new Properties();

        if (url == null) {
            return null;
        }

        if (!StringUtils.startsWithIgnoreCase(url, URL_PREFIX)
                && !StringUtils.startsWithIgnoreCase(url, MXJ_URL_PREFIX)
                && !StringUtils.startsWithIgnoreCase(url,
                        LOADBALANCE_URL_PREFIX)
                && !StringUtils.startsWithIgnoreCase(url,
                        REPLICATION_URL_PREFIX)) { //$NON-NLS-1$

            return null;
        }
        //......

2.SPI示例

3.JDK SPI缺點(diǎn)

  • 需要遍歷所有的實(shí)現(xiàn),并實(shí)例化,然后我們?cè)谘h(huán)中才能找到我們需要的實(shí)現(xiàn)。
  • 配置文件中只是簡(jiǎn)單的列出了所有的擴(kuò)展實(shí)現(xiàn),而沒(méi)有給他們命名。導(dǎo)致在程序中很難去準(zhǔn)確的引用它們。
  • 擴(kuò)展如果依賴其他的擴(kuò)展,做不到自動(dòng)注入和裝配
  • 不提供類似于Spring的AOP功能
  • 擴(kuò)展很難和其他的框架集成,比如擴(kuò)展里面依賴了一個(gè)Spring bean,原生的Java SPI不支持
    所以Java SPI應(yīng)付一些簡(jiǎn)單的場(chǎng)景是可以的,但對(duì)于Dubbo,它的功能還是比較弱的。Dubbo對(duì)原生SPI機(jī)制進(jìn)行了一些擴(kuò)展。接下來(lái),我們就更深入地了解下Dubbo的SPI機(jī)制。

4.dubbo SPI

擴(kuò)展點(diǎn)(Extension Point)
是一個(gè)Java的接口。
擴(kuò)展(Extension)
擴(kuò)展點(diǎn)的實(shí)現(xiàn)類。
擴(kuò)展實(shí)例
擴(kuò)展點(diǎn)實(shí)現(xiàn)類的實(shí)例。
擴(kuò)展自適應(yīng)實(shí)例(Extension Adaptive Instance)
第一次接觸這個(gè)概念時(shí),可能不太好理解(我第一次也是這樣的...)。如果稱它為擴(kuò)展代理類,可能更好理解些。擴(kuò)展的自適應(yīng)實(shí)例其實(shí)就是一個(gè)Extension的代理,它實(shí)現(xiàn)了擴(kuò)展點(diǎn)接口。在調(diào)用擴(kuò)展點(diǎn)的接口方法時(shí),會(huì)根據(jù)實(shí)際的參數(shù)來(lái)決定要使用哪個(gè)擴(kuò)展。比如一個(gè)IRepository的擴(kuò)展點(diǎn),有一個(gè)save方法。有兩個(gè)實(shí)現(xiàn)MysqlRepository和MongoRepository。IRepository的自適應(yīng)實(shí)例在調(diào)用接口方法的時(shí)候,會(huì)根據(jù)save方法中的參數(shù),來(lái)決定要調(diào)用哪個(gè)IRepository的實(shí)現(xiàn)。如果方法參數(shù)中有repository=mysql,那么就調(diào)用MysqlRepository的save方法。如果repository=mongo,就調(diào)用MongoRepository的save方法。和面向?qū)ο蟮难舆t綁定很類似。為什么Dubbo會(huì)引入擴(kuò)展自適應(yīng)實(shí)例的概念呢?

Dubbo中的配置有兩種,一種是固定的系統(tǒng)級(jí)別的配置,在Dubbo啟動(dòng)之后就不會(huì)再改了。還有一種是運(yùn)行時(shí)的配置,可能對(duì)于每一次的RPC,這些配置都不同。比如在xml文件中配置了超時(shí)時(shí)間是10秒鐘,這個(gè)配置在Dubbo啟動(dòng)之后,就不會(huì)改變了。但針對(duì)某一次的RPC調(diào)用,可以設(shè)置它的超時(shí)時(shí)間是30秒鐘,以覆蓋系統(tǒng)級(jí)別的配置。對(duì)于Dubbo而言,每一次的RPC調(diào)用的參數(shù)都是未知的。只有在運(yùn)行時(shí),根據(jù)這些參數(shù)才能做出正確的決定。
很多時(shí)候,我們的類都是一個(gè)單例的,比如Spring的bean,在Spring bean都實(shí)例化時(shí),如果它依賴某個(gè)擴(kuò)展點(diǎn),但是在bean實(shí)例化時(shí),是不知道究竟該使用哪個(gè)具體的擴(kuò)展實(shí)現(xiàn)的。這時(shí)候就需要一個(gè)代理模式了,它實(shí)現(xiàn)了擴(kuò)展點(diǎn)接口,方法內(nèi)部可以根據(jù)運(yùn)行時(shí)參數(shù),動(dòng)態(tài)的選擇合適的擴(kuò)展實(shí)現(xiàn)。而這個(gè)代理就是自適應(yīng)實(shí)例。 自適應(yīng)擴(kuò)展實(shí)例在Dubbo中的使用非常廣泛,Dubbo中,每一個(gè)擴(kuò)展都會(huì)有一個(gè)自適應(yīng)類,如果我們沒(méi)有提供,Dubbo會(huì)使用字節(jié)碼工具為我們自動(dòng)生成一個(gè)。所以我們基本感覺(jué)不到自適應(yīng)類的存在。后面會(huì)有例子說(shuō)明自適應(yīng)類是怎么工作的。
@SPI
@SPI注解作用于擴(kuò)展點(diǎn)的接口上,表明該接口是一個(gè)擴(kuò)展點(diǎn)。可以被Dubbo的ExtentionLoader加載。如果沒(méi)有此ExtensionLoader調(diào)用會(huì)異常。
@Adaptive
@Adaptive注解用在擴(kuò)展接口的方法上。表示該方法是一個(gè)自適應(yīng)方法。Dubbo在為擴(kuò)展點(diǎn)生成自適應(yīng)實(shí)例時(shí),如果方法有@Adaptive注解,會(huì)為該方法生成對(duì)應(yīng)的代碼。方法內(nèi)部會(huì)根據(jù)方法的參數(shù),來(lái)決定使用哪個(gè)擴(kuò)展。
ExtentionLoader
類似于Java SPI的ServiceLoader,負(fù)責(zé)擴(kuò)展的加載和生命周期維護(hù)。
擴(kuò)展別名
和Java SPI不同,Dubbo中的擴(kuò)展都有一個(gè)別名,用于在應(yīng)用中引用它們。比如

random=com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance 
roundrobin=com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance 

其中的random,roundrobin就是對(duì)應(yīng)擴(kuò)展的別名。這樣我們?cè)谂渲梦募惺褂胷andom或roundrobin就可以了。
路徑
和Java SPI從/META-INF/services目錄加載擴(kuò)展配置類似,Dubbo也會(huì)從以下路徑去加載擴(kuò)展配置文件:

  • META-INF/dubbo/internal
  • META-INF/dubbo
  • META-INF/services

5.Dubbo的LoadBalance擴(kuò)展點(diǎn)解讀

在了解了Dubbo的一些基本概念后,讓我們一起來(lái)看一個(gè)Dubbo中實(shí)際的擴(kuò)展點(diǎn),對(duì)這些概念有一個(gè)更直觀的認(rèn)識(shí)。
我們選擇的是Dubbo中的\color{red}{LoadBalance}擴(kuò)展點(diǎn)。Dubbo中的一個(gè)服務(wù),通常有多個(gè)Provider,consumer調(diào)用服務(wù)時(shí),需要在多個(gè)Provider中選擇一個(gè)。這就是一個(gè)LoadBalance。我們一起來(lái)看看在Dubbo中,LoadBalance是如何成為一個(gè)擴(kuò)展點(diǎn)的。

package com.alibaba.dubbo.rpc.cluster;
@SPI("random")
public interface LoadBalance {
    @Adaptive({"loadbalance"})
    <T> Invoker<T> select(List<Invoker<T>> var1, URL var2, Invocation var3) throws RpcException;
}

LoadBalance接口只有一個(gè)select方法。select方法從多個(gè)invoker中選擇其中一個(gè)。上面代碼中和Dubbo SPI相關(guān)的元素有:

  • @SPI("random") @SPI作用于LoadBalance接口,表示接口LoadBalance是一個(gè)擴(kuò)展點(diǎn)。如果沒(méi)有@SPI注解,試圖去加載擴(kuò)展時(shí),會(huì)拋出異常。@SPI注解有一個(gè)參數(shù),該參數(shù)表示該擴(kuò)展點(diǎn)的默認(rèn)實(shí)現(xiàn)的別名。如果沒(méi)有顯示的指定擴(kuò)展,就使用默認(rèn)實(shí)現(xiàn)。默認(rèn)實(shí)現(xiàn)是"random",是一個(gè)隨機(jī)負(fù)載均衡的實(shí)現(xiàn)。 random的定義在配置文件META-INF/dubbo/internal/com.alibaba.dubbo.rpc.cluster.LoadBalance中:
random=com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance 
roundrobin=com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance 
leastactive=com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance 
consistenthash=com.alibaba.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance

可以看到文件中定義了4個(gè)LoadBalance的擴(kuò)展實(shí)現(xiàn)。由于負(fù)載均衡的實(shí)現(xiàn)不是本次的內(nèi)容,這里就不過(guò)多說(shuō)明。只用知道Dubbo提供了4種負(fù)載均衡的實(shí)現(xiàn),我們可以通過(guò)xml文件,properties文件,JVM參數(shù)顯式的指定一個(gè)實(shí)現(xiàn)。如果沒(méi)有,默認(rèn)使用隨機(jī)。

  • @Adaptive("loadbalance")@Adaptive注解修飾select方法,表明方法select方法是一個(gè)可自適應(yīng)的方法。Dubbo會(huì)自動(dòng)生成該方法對(duì)應(yīng)的代碼。當(dāng)調(diào)用select方法時(shí),會(huì)根據(jù)具體的方法參數(shù)來(lái)決定調(diào)用哪個(gè)擴(kuò)展實(shí)現(xiàn)的select方法。@Adaptive注解的參數(shù)loadbalance表示方法參數(shù)中的loadbalance的值作為實(shí)際要調(diào)用的擴(kuò)展實(shí)例。 但奇怪的是,我們發(fā)現(xiàn)select的方法中并沒(méi)有l(wèi)oadbalance參數(shù),那怎么獲取loadbalance的值呢?select方法中還有一個(gè)URL類型的參數(shù),Dubbo就是從URL中獲取loadbalance的值的。這里涉及到Dubbo的URL總線模式,簡(jiǎn)單說(shuō),URL中包含了RPC調(diào)用中的所有參數(shù)。URL類中有一個(gè)Map \color{red}{parameters}字段,parameters中就包含了loadbalance。
LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(loadbalanceName); 

使用ExtensionLoader.getExtensionLoader(LoadBalance.class)方法獲取一個(gè)ExtensionLoader的實(shí)例,然后調(diào)用getExtension,傳入一個(gè)擴(kuò)展的別名來(lái)獲取對(duì)應(yīng)的擴(kuò)展實(shí)例。

6.自定義一個(gè)LoadBalance擴(kuò)展

1.實(shí)現(xiàn)LoadBalance接口,首先,編寫一個(gè)自己實(shí)現(xiàn)的LoadBalance,因?yàn)槭菫榱搜菔綝ubbo的擴(kuò)展機(jī)制,而不是LoadBalance的實(shí)現(xiàn),所以這里L(fēng)oadBalance的實(shí)現(xiàn)非常簡(jiǎn)單,選擇第一個(gè)invoker,并在控制臺(tái)輸出一條日志。

public class DemoLoadBalance implements LoadBalance {
    @Override
    public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
        System.out.println("DemoLoadBalance : Select the first invoker...");
        return invokers.get(0);
    }
}

2.META-INF/dubbo下添加擴(kuò)展配置文件

demo=com.dubbo.spi.demo.consumer.DemoLoadBalance
  1. 通過(guò)上面的兩步,已經(jīng)添加了一個(gè)名字為demo的LoadBalance實(shí)現(xiàn),并在配置文件中進(jìn)行了相應(yīng)的配置。接下來(lái),需要顯式的告訴Dubbo使用demo的負(fù)載均衡實(shí)現(xiàn)。如果是通過(guò)spring的方式使用Dubbo,可以在xml文件中進(jìn)行設(shè)置。
 <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
 <dubbo:reference id="helloService" interface="com.dubbo.spi.demo.api.IHelloService" loadbalance="demo" />

啟動(dòng)Dubbo
啟動(dòng)Dubbo,調(diào)用一次IHelloService,可以看到控制臺(tái)會(huì)輸出一條DemoLoadBalance: Select the first invoker...日志。說(shuō)明Dubbo的確是使用了我們自定義的LoadBalance。

zk

image.png

控制臺(tái)

服務(wù)提供者1

服務(wù)提供者2

總結(jié)dubbo SPI

  • 對(duì)Dubbo進(jìn)行擴(kuò)展,不需要改動(dòng)Dubbo的源碼
  • 自定義的Dubbo的擴(kuò)展點(diǎn)實(shí)現(xiàn),是一個(gè)普通的Java類,Dubbo沒(méi)有引入任何Dubbo特有的元素,對(duì)代碼侵入性幾乎為零。
  • 將擴(kuò)展注冊(cè)到Dubbo中,只需要在ClassPath中添加配置文件。使用簡(jiǎn)單。而且不會(huì)對(duì)現(xiàn)有代碼造成影響。符合開(kāi)閉原則。
  • Dubbo的擴(kuò)展機(jī)制支持IoC,AoP等高級(jí)功能
  • Dubbo的擴(kuò)展機(jī)制能很好的支持第三方IOC容器,默認(rèn)支持Spring Bean,可自己擴(kuò)展來(lái)支持其他容器,比如Google的Guice。
  • 切換擴(kuò)展點(diǎn)的實(shí)現(xiàn),只需要在配置文件中修改具體的實(shí)現(xiàn),不需要改代碼。使用方便。

轉(zhuǎn)載鏈接
轉(zhuǎn)載鏈接
dubbo各協(xié)議比較

Dubbo作者親述:那些輝煌、沉寂與重生的故事

dubbo官方文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 從今天開(kāi)始,將會(huì)逐步介紹關(guān)于DUbbo的有關(guān)知識(shí)。首先先簡(jiǎn)單介紹一下DUbbo的整體概述。 概述 Dubbo是SO...
    九點(diǎn)半的馬拉閱讀 1,416評(píng)論 0 0
  • 文章要點(diǎn) 什么是spi dubbo為什么實(shí)現(xiàn)自己的spi dubbo的adaptive機(jī)制 dubbo的IOC和A...
    青芒v5閱讀 748評(píng)論 0 4
  • 0 前言 站在一個(gè)框架作者的角度來(lái)說(shuō),定義一個(gè)接口,自己默認(rèn)給出幾個(gè)接口的實(shí)現(xiàn)類,同時(shí) 允許框架的使用者也能夠自定...
    七寸知架構(gòu)閱讀 16,427評(píng)論 3 66
  • 在 Dubbo 中,SPI 貫穿在整個(gè) Dubbo 的核心,所以把 Dubbo 里面用得比較多的 SPI 機(jī)制做一...
    WEIJAVA閱讀 555評(píng)論 0 1
  • 站在十字街頭,不知向何方邁出腳步, 細(xì)雨落在頭頂肩頭慢慢浸濕發(fā)絲與薄衫, 皮膚對(duì)雨的觸感使她微微顫栗, 這是夏日的...
    小雨的海閱讀 543評(píng)論 7 10

友情鏈接更多精彩內(nèi)容