1. 程式人生 > >DUBBO SPI部分原始碼淺析

DUBBO SPI部分原始碼淺析

    private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

這裡先上核心程式碼。
在分析dubbo原始碼時,我們發現每個config類裡面都有這段程式碼。
首先,protocal類帶有spi註解,我們可以確認,預設使用的DUBBO-PROTOCAL作為預設擴充套件點。

/*
 * Copyright 1999-2011 Alibaba Group.
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.dubbo.rpc; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.common.extension.Adaptive; import com.alibaba.dubbo.common.extension.SPI; /** * Protocol. (API/SPI, Singleton, ThreadSafe) * * @author william.liangf */ @SPI("dubbo") public interface Protocol { /** * 獲取預設埠,當用戶沒有配置埠時使用。 * * @return
預設埠 */
int getDefaultPort(); /** * 暴露遠端服務:<br> * 1. 協議在接收請求時,應記錄請求來源方地址資訊:RpcContext.getContext().setRemoteAddress();<br> * 2. export()必須是冪等的,也就是暴露同一個URL的Invoker兩次,和暴露一次沒有區別。<br> * 3. export()傳入的Invoker由框架實現並傳入,協議不需要關心。<br> * * @param
<T> 服務的型別 * @param invoker 服務的執行體 * @return exporter 暴露服務的引用,用於取消暴露 * @throws RpcException 當暴露服務出錯時丟擲,比如埠已佔用 */
@Adaptive <T> Exporter<T> export(Invoker<T> invoker) throws RpcException; /** * 引用遠端服務:<br> * 1. 當用戶呼叫refer()所返回的Invoker物件的invoke()方法時,協議需相應執行同URL遠端export()傳入的Invoker物件的invoke()方法。<br> * 2. refer()返回的Invoker由協議實現,協議通常需要在此Invoker中傳送遠端請求。<br> * 3. 當url中有設定check=false時,連線失敗不能丟擲異常,並內部自動恢復。<br> * * @param <T> 服務的型別 * @param type 服務的型別 * @param url 遠端服務的URL地址 * @return invoker 服務的本地代理 * @throws RpcException 當連線服務提供方失敗時丟擲 */ @Adaptive <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException; /** * 釋放協議:<br> * 1. 取消該協議所有已經暴露和引用的服務。<br> * 2. 釋放協議所佔用的所有資源,比如連線和埠。<br> * 3. 協議在釋放後,依然能暴露和引用新的服務。<br> */ void destroy(); }

首先通過類載入器獲取一個Adaptive例項,先去快取裡面找,看有沒有這個例項,如果沒有,使用synchronized同步再次獲取(為什麼要再次獲取?可能上一個程序已經在同步程式碼內set進去了,而這裡狀態還沒有實時更新);如果沒有,新建立一個Adaptive例項,放入快取。

 @SuppressWarnings("unchecked")
    public T getAdaptiveExtension() {
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
            if (createAdaptiveInstanceError == null) {
                synchronized (cachedAdaptiveInstance) {
                    instance = cachedAdaptiveInstance.get();
                    if (instance == null) {
                        try {
                            instance = createAdaptiveExtension();
                            cachedAdaptiveInstance.set(instance);
                        } catch (Throwable t) {
                            createAdaptiveInstanceError = t;
                            throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
                        }
                    }
                }
            } else {
                throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
            }
        }

        return (T) instance;
    }

建立程式碼如下,這裡通過1反射獲取擴充套件點如果有直接返回,否則通過擴充套件點類,2compile出類檔案。
在1過程中,不斷將擴充套件點的所有引數的型別,引數的名稱,通過擴充套件點工廠中去拿,有值就注入該擴充套件點中。

 try {
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);
        }

最後,他的快取部分我們可以看到在擴充套件點載入類ExtensionLoader中可以看到有一個Holder例項維護的快取類

    private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();

這個快取類中的Object用volatile修飾,是一個共享變數。

動態類實現

通過類compile出來的類檔案如下:可以看出只有標記了Adaptive註解的會在執行時決定擴充套件點的實現

package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implementscom.alibaba.dubbo.rpc.Protocol {
public void destroy() {
throw new UnsupportedOperationException("method public abstract
void com.alibaba.dubbo.rpc.Protocol.destroy() of interface
com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort()
{
throw new UnsupportedOperationException("method public abstract
int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface
com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public com.alibaba.dubbo.rpc.Exporter
export(com.alibaba.dubbo.rpc.Invoker arg0) throws
com.alibaba.dubbo.rpc.Invoker
{
if (arg0 == null) throw new
IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument ==
null");
if (arg0.getUrl() == null) throw new
IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument
getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = ( url.getProtocol() == null ? "dubbo" :
url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get
extension(com.alibaba.dubbo.rpc.Protocol) name from url(" +
url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension =
(com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(co
m.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0,
com.alibaba.dubbo.common.URL arg1) throws java.lang.Class
{
if (arg1 == null) throw new IllegalArgumentException("url ==
null");
com.alibaba.dubbo.common.URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" :
url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to getextension(com.alibaba.dubbo.rpc.Protocol) name from url(" +
url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension =
(com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(co
m.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
}

下面分析獲取擴充套件點:

 /**
     * 返回指定名字的擴充套件。如果指定名字的擴充套件不存在,則拋異常 {@link IllegalStateException}.
     *
     * @param name
     * @return
     */
    @SuppressWarnings("unchecked")
    public T getExtension(String name) {
        if (name == null || name.length() == 0)
            throw new IllegalArgumentException("Extension name == null");
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder<Object>());
            holder = cachedInstances.get(name);
        }
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

再看createExtension:

@SuppressWarnings("unchecked")
    private T createExtension(String name) {
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (wrapperClasses != null && wrapperClasses.size() > 0) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                    type + ")  could not be instantiated: " + t.getMessage(), t);
        }
    }

未完待續……

相關推薦

DUBBO SPI部分原始碼淺析

private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); 這裡先上核心程式碼。

Android應用ViewDragHelper詳解及部分原始碼淺析

1 背景 很久沒有更新部落格了,忙裡偷閒產出一篇。寫這片文章主要是去年專案中的一個需求,當時三下五除二的將其實現了,但是原始碼的閱讀卻一直扔在那遲遲沒有時間理會,現在揀起來看看吧,否則心裡一直不踏實。 關於啥是ViewDragHelper,這裡不再

【QT】 QThread部分原始碼淺析

本文章挑出QThread原始碼中部分重點程式碼來說明QThread啟動到結束的過程是怎麼排程的。其次因為到了Qt4.4版本,Qt的多執行緒就有所變化,所以本章會以Qt4.0.1和Qt5.6.2版本的原始碼來進行淺析。 # QThread類的定義原始碼 Qt4.0.1版本原始碼: ```cpp #ifnde

Dubbo SPI 機制原始碼分析(基於2.7.7)

Dubbo SPI 機制涉及到 `@SPI`、`@Adaptive`、`@Activate` 三個註解,ExtensionLoader 作為 Dubbo SPI 機制的核心負責載入和管理擴充套件點及其實現。本文以 ExtensionLoader 的原始碼作為分析主線,進而引出三個註解的作用和工作機制。 Ex

Dubbo原始碼閱讀系列】之 Dubbo SPI 機制

最近抽空開始了 Dubbo 原始碼的閱讀之旅,希望可以通過寫文章的方式記錄和分享自己對 Dubbo 的理解。如果在本文出現一些紕漏或者錯誤之處,也希望大家不吝指出。 Dubbo SPI 介紹 Java SPI 在閱讀本文之前可能需要你對 Java SPI(Service Provider In

dubbo原始碼淺析(五)-遠端服務呼叫流程

消費端呼叫遠端服務介面時,使用上和呼叫普通的java介面是沒有任何區別,但是服務消費者和提供者是跨JVM和主機的,客戶端如何封裝請求讓服務端理解請求並且解析服務端返回的介面呼叫結果,服務端如何解析客戶端的請求並且向客戶端返回呼叫結果,這些框架是如何實現的,下面就

dubbo原始碼淺析(二)-標籤解析

前面瞭解了dubbo的外掛化機制之後,接下來進入正題,研究一下dubbo的核心原理,由於dubbo的功能配置較多,為了更高效的研讀程式碼,在閱讀的過程中儘量忽略一些細節,重點關注它的主幹流程,主幹瞭解清楚之後再去分析它的一些細節功能就更輕鬆了,否則容易陷入各種細

weex原始碼淺析(Android部分)一

最近預研阿里的weex方案,同最近比較火的ReactNative比較類似,提供了一個一次編寫三端共用的解決方案. 下面引用官方的描述 Weex 是一套簡單易用的跨平臺開發方案,能以 web 的開發體驗構建高效能、可擴充套件的 native 應用,為了做

dubbo原始碼淺析(四)-服務消費者初始化

在分析標籤解析的時候知道框架會把dubbo:reference解析成一個ReferenceBean,它是一個FactoryBean,消費者的初始化在它的init方法中執行,這個方法在兩種情況下會被呼叫: 1. 消費者設定了立即初始化(init屬性設定成tr

Dubbo SPI

獲取 meta class filter 模式 引用 bsp 函數 adapt Dubbo SPI是Dubbo留的擴展點 例如Dubbo的 Protocol 。 ExtensionLoader.getExtensionLoader(Protocol.class).getA

重新去認識HashMap(Java8原始碼淺析

加入新公司後一直忙於專案,瘋狂加班,斷更了N個月,一直沒時間去管理自己所學習的新的知識(說白了就是懶。。。),前些天在頭條上看到了一篇關於jdk5,6,7,8,9的一些區別的文章,雖然有所瞭解,但由於自己的專案中用的依舊還是1.6,因此並沒有很多機會去了解一些新版本的一些特性(說白了還是

flink on yarn部分原始碼解析 (FLIP-6 new mode)

我們在https://www.cnblogs.com/dongxiao-yang/p/9403427.html文章裡分析了flink提交single job到yarn叢集上的程式碼,flink在1.5版本後對整個框架的deploy方式重構了全新的流程(參考https://cwiki.apache.org/co

Android Hook框架adbi原始碼淺析(二)

二、libbase 其實上面載入完SO庫後,hook的功能我們完全可以自己在動態庫中實現。而adbi作者為了方便我們使用,編寫了一個通用的hook框架工具即libbase庫。libbase依然在解決兩個問題:1.獲取要hook的目標函式地址;2.給函式打二進位制補丁即inline hook。 關於獲取ho

Android Hook框架adbi原始碼淺析(一)

adbi(The Android Dynamic Binary Instrumentation Toolkit)是一個Android平臺通用hook框架,基於動態庫注入與inline hook技術實現。該框架由兩個主要模組構成,1.hijack負責將動態庫注入到目標程序;2.libbase提供動態庫本身,它實

小而美的Promise庫——promiz原始碼淺析

背景 在上一篇部落格[[譯]前端基礎知識儲備——Promise/A+規範](https://segmentfault.com/a/11...,我們介紹了Promise/A+規範的具體條目。在本文中,我們來選擇了promiz,讓大家來看下一個具體的Promise庫的內部程式碼是如何運作的。 promiz是一

linux kernel(二)原始碼淺析

目錄 kernel(二)原始碼淺析 建立工程 啟動簡析 head.s 入口點 查詢處理器 查詢機器ID 啟動MMU 其他操作 start_kernel 處理命令列

FileProvider使用以及原始碼淺析

1. FileProvider的使用 1.1 AndroidManifest.xml中定義 <provider android:name="android.support.v4.content.FileProvider" a

[JDK1.7]LinkedHashMap原始碼淺析

引言 HashMap 是一個無序的 Map,因為每次根據 key 的 hashcode 對映到 Entry 陣列上,所以遍歷出來的順序並不是寫入的順序。LinkedHashMap 的底層是繼承於 HashMap 實現的,由一個雙向連結串列所構成; 一、初識LinkedH

Django-Rest-Framework 許可權管理原始碼淺析

許可權管理簡單介紹 在django的views中不論是用類方式還是用裝飾器方式來使用rest框架,django_rest_frame實現許可權管理都需要兩個東西的配合:authentication_classes 和 permission_classes # 方式1: 裝飾器 from rest_fram

HashMap部分原始碼及疑難問題解析(JDK8)

HashMap是Map(雙列集合)體系中極為重要的一個集合類,執行緒不安全,若需要執行緒安全則使用ConcurrentSkipListMap,較TreeMap擁有更好的查詢、插入效率,具體效率對比請看 Java Map遍歷方式的選——TreeMap、HashMap的key、value遍歷與效率分