從今天開始,將會(huì)逐步介紹關(guān)于DUbbo的有關(guān)知識(shí)。首先先簡單介紹一下DUbbo的整體概述。
概述
Dubbo是SOA(面向服務(wù)架構(gòu))服務(wù)治理方案的核心框架。用于分布式調(diào)用,其重點(diǎn)在于分布式的治理。
簡單的來說,可以把它分為四個(gè)角色。服務(wù)提供方(Provider)、服務(wù)消費(fèi)方(Consumer)、注冊(cè)中心和監(jiān)控中心。通過注冊(cè)中心對(duì)服務(wù)進(jìn)行注冊(cè)和訂閱,通過監(jiān)控中心對(duì)服務(wù)進(jìn)行監(jiān)控。
*核心功能 *
- Remoting:遠(yuǎn)程通訊,提供對(duì)多種NIO框架抽象封裝,包括“同步轉(zhuǎn)異步”和“請(qǐng)求-響應(yīng)”模式的信息交換方式。
- Cluster: 服務(wù)框架,提供基于接口方法的透明遠(yuǎn)程過程調(diào)用,包括多協(xié)議支持,以及軟負(fù)載均衡,失敗容錯(cuò),地址路由,動(dòng)態(tài)配置等集群支持。
- Registry: 服務(wù)注冊(cè),基于注冊(cè)中心目錄服務(wù),使服務(wù)消費(fèi)方能動(dòng)態(tài)的查找服務(wù)提供方,使地址透明,使服務(wù)提供方可以平滑增加或減少機(jī)器。
*Dubbo組件角色 *
Provider: 暴露服務(wù)的服務(wù)提供方
Consumer: 調(diào)用遠(yuǎn)程服務(wù)的服務(wù)消費(fèi)方
Registry: 服務(wù)注冊(cè)與發(fā)現(xiàn)的注冊(cè)中心
Monitor: 統(tǒng)計(jì)服務(wù)的調(diào)用次數(shù)和調(diào)用時(shí)間的監(jiān)控中心
Container: 服務(wù)運(yùn)行容器,常見的容器有Spring容器
調(diào)用關(guān)系:
- 服務(wù)容器負(fù)責(zé)啟動(dòng),加載,運(yùn)行服務(wù)提供者
- 服務(wù)提供者在啟動(dòng)時(shí),向注冊(cè)中心注冊(cè)自己提供的服務(wù)。
- 服務(wù)消費(fèi)者在啟動(dòng)時(shí),向注冊(cè)中心訂閱自己所需的服務(wù)。
- 注冊(cè)中心返回服務(wù)提供者地址列表給消費(fèi)者,如果有變更,注冊(cè)中心將基于長連接推送變更數(shù)據(jù)給消費(fèi)者。
- 服務(wù)消費(fèi)者,從提供者地址列表中,基于軟負(fù)載均衡算法,選一臺(tái)提供者進(jìn)行調(diào)用,如果調(diào)用失敗,再選另一臺(tái)調(diào)用。
-
服務(wù)消費(fèi)者和提供者,在內(nèi)存中累計(jì)調(diào)用次數(shù)和調(diào)用時(shí)間,定時(shí)每分鐘發(fā)送一次統(tǒng)計(jì)數(shù)據(jù)到監(jiān)控中心Monitor。
1.png
SPI(Service Provider Interfaces)
它是Java提供的一套用來被第三方實(shí)現(xiàn)或者擴(kuò)展的API,它可以用來啟用框架擴(kuò)展和替換組件。在JDK文檔中,它這樣解釋道:
A service is a well-known set of interfaces and (usually abstract) classes. A service provider is a specific implementation of a service.
在面向?qū)ο蟮脑O(shè)計(jì)里面,模塊之間推薦是基于接口編程,而不是對(duì)實(shí)現(xiàn)類進(jìn)行硬編碼,這樣做也是為了模塊設(shè)計(jì)的可拔插原則。為了在模塊裝配的時(shí)候不再程序里指明是那個(gè)實(shí)現(xiàn),就需要一種服務(wù)發(fā)現(xiàn)的機(jī)制,jDK的SPI就是為某個(gè)接口尋找服務(wù)實(shí)現(xiàn)。

Java SPI實(shí)際上就是基于接口的編程+策略模式+配置文件組合實(shí)現(xiàn)的動(dòng)態(tài)加載機(jī)制。
它為某個(gè)接口尋找服務(wù)實(shí)現(xiàn)的機(jī)制。有點(diǎn)類似IOC的思想,就是將裝配的控制權(quán)移到程序之外,在模式化設(shè)計(jì)中這個(gè)機(jī)制尤其重要,所以它的核心思想是解耦
使用場景
- 數(shù)據(jù)庫驅(qū)動(dòng)加載接口實(shí)現(xiàn)類的加載
JDBC加載不同類型數(shù)據(jù)庫的驅(qū)動(dòng) - 日志門面接口實(shí)現(xiàn)類加載
SLF4J加載不同提供商的日志實(shí)現(xiàn)類 - Spring
- Dubbo
使用說明
- 當(dāng)服務(wù)提供者提供了接口的一種具體實(shí)現(xiàn)后,在jar包的META-INF/service目錄下創(chuàng)建一個(gè)以"接口全限定名"為命名的文件,內(nèi)容為實(shí)現(xiàn)類的全限定名。
- 接口實(shí)現(xiàn)類所在的jar包放在主程序的classpath中
- 主程序通過java.util.ServiceLoader動(dòng)態(tài)加載實(shí)現(xiàn)模板,它通過掃描META-INF/services目錄下的配置文件找到實(shí)現(xiàn)類的全限定名,把類加載到JVM
- SPI的實(shí)現(xiàn)類必須攜帶一個(gè)不帶參數(shù)的構(gòu)造方法
public final class ServiceLoader<S> implements Iterable<S>
{
private static final String PREFIX = "META-INF/services/";
// 代表被加載的類或者接口
private final Class<S> service;
// 用于定位、加載和實(shí)例化providers的類加載器
private final ClassLoader loader;
// 創(chuàng)建ServiceLoader時(shí)采用的訪問控制上下文
private final AccessControlContext acc;
// 緩存providers,按照實(shí)例化的順序排序
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
// 懶查找迭代器
private LazyIterator lookupIterator;
//重新加載,就相當(dāng)于重新創(chuàng)建ServiceLoader了,用于新的服務(wù)提供者安裝到正在運(yùn)行的Java虛擬機(jī)
public void reload() {
//清空緩存中所有已實(shí)例化的服務(wù)提供者
providers.clear();
//新建一個(gè)迭代器,該迭代器會(huì)從頭查找和實(shí)例化服務(wù)提供者。
lookupIterator = new LazyIterator(service, loader);
}
/**
** 私有構(gòu)造器
** 使用指定的類加載器和服務(wù)創(chuàng)建服務(wù)加載器
** 如果沒有指定類加載器,使用系統(tǒng)類加載器,就是應(yīng)用類加載器
**/
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();
}
//解析失敗處理的方法
private static void fail(Class<?> service, String msg, Throwable cause)
throws ServiceConfigurationError
{
throw new ServiceConfigurationError(service.getName() + ": " + msg,
cause);
}
private static void fail(Class<?> service, String msg)
throws ServiceConfigurationError
{
throw new ServiceConfigurationError(service.getName() + ": " + msg);
}
private static void fail(Class<?> service, URL u, int line, String msg)
throws ServiceConfigurationError
{
fail(service, u + ":" + line + ": " + msg);
}
//解析服務(wù)提供者配置文件中的一行
//首先去掉注釋檢驗(yàn),然后保存
//返回下一行行號(hào)
//重復(fù)的配置項(xiàng)不會(huì)被保存
private int parseLine(Class<?> service, URL u, BufferedReader r, int lc,
List<String> names)
throws IOException, ServiceConfigurationError
{
String ln = r.readLine();
if (ln == null) {
return -1;
}
int ci = ln.indexOf('#');
if (ci >= 0) ln = ln.substring(0, ci);
ln = ln.trim();
int n = ln.length();
if (n != 0) {
if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
fail(service, u, lc, "Illegal configuration-file syntax");
int cp = ln.codePointAt(0);
if (!Character.isJavaIdentifierStart(cp))
fail(service, u, lc, "Illegal provider-class name: " + ln);
for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
cp = ln.codePointAt(i);
if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
fail(service, u, lc, "Illegal provider-class name: " + ln);
}
if (!providers.containsKey(ln) && !names.contains(ln))
names.add(ln);
}
return lc + 1;
}
//解析配置文件,解析指定的url配置文件
//使用parseLine方法進(jìn)行解析,未被實(shí)例化的服務(wù)提供者會(huì)被保存到緩存中。
private Iterator<String> parse(Class<?> service, URL u)
throws ServiceConfigurationError
{
InputStream in = null;
BufferedReader r = null;
ArrayList<String> names = new ArrayList<>();
try {
in = u.openStream();
r = new BufferedReader(new InputStreamReader(in, "utf-8"));
int lc = 1;
while ((lc = parseLine(service, u, r, lc, names)) >= 0);
} catch (IOException x) {
fail(service, "Error reading configuration file", x);
} finally {
try {
if (r != null) r.close();
if (in != null) in.close();
} catch (IOException y) {
fail(service, "Error closing configuration file", y);
}
}
return names.iterator();
}
//服務(wù)提供者查找的迭代器
private class LazyIterator implements Iterator<S>
{
//服務(wù)提供者接口
Class<S> service;
//類加載器
ClassLoader loader;
//保存實(shí)現(xiàn)類的url
Enumeration<URL> configs = null;
//保存實(shí)現(xiàn)類的全名
Iterator<String> pending = null;
//迭代器中下一個(gè)實(shí)現(xiàn)類的全名
String nextName = null;
private LazyIterator(Class<S> service, ClassLoader loader) {
this.service = service;
this.loader = loader;
}
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;
}
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
}
public boolean hasNext() {
if (acc == null) {
return hasNextService();
} else {
PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
public Boolean run() { return hasNextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
public S next() {
if (acc == null) {
return nextService();
} else {
PrivilegedAction<S> action = new PrivilegedAction<S>() {
public S run() { return nextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
//獲取迭代器
//返回遍歷服務(wù)提供者的迭代器
//以懶加載的方式加載可用的服務(wù)提供者
//懶加載的實(shí)現(xiàn)是:解析配置文件和實(shí)例化服務(wù)提供者的工作由迭代器本身完成
public Iterator<S> iterator() {
return new Iterator<S>() {
Iterator<Map.Entry<String,S>> knownProviders
= providers.entrySet().iterator();
public boolean hasNext() {
if (knownProviders.hasNext())
return true;
return lookupIterator.hasNext();
}
public S next() {
if (knownProviders.hasNext())
return knownProviders.next().getValue();
return lookupIterator.next();
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
//為指定的服務(wù)使用指定的類加載器來創(chuàng)建一個(gè)ServiceLoader
public static <S> ServiceLoader<S> load(Class<S> service,
ClassLoader loader)
{
return new ServiceLoader<>(service, loader);
}
//使用線程上下文的類加載器來創(chuàng)建一個(gè)ServiceLoader
public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
//使用擴(kuò)展類加載器為指定的服務(wù)創(chuàng)建ServiceLoader
//只能找到并加載已經(jīng)安裝到當(dāng)前Java虛擬機(jī)中的服務(wù)提供者,應(yīng)用程序類路徑中的服務(wù)提供者將被忽略
public static <S> ServiceLoader<S> loadInstalled(Class<S> service) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
ClassLoader prev = null;
while (cl != null) {
prev = cl;
cl = cl.getParent();
}
return ServiceLoader.load(service, prev);
}
/**
* Returns a string describing this service.
*
* @return A descriptive string
*/
public String toString() {
return "java.util.ServiceLoader[" + service.getName() + "]";
}
}
ServiceLoader不是實(shí)例化以后,就去讀取文件的具體實(shí)現(xiàn)。而是等到使用迭代器去遍歷的時(shí)候,才會(huì)加載對(duì)應(yīng)的配置文件去解析,調(diào)用hasNext方法時(shí)就去加載配置文件進(jìn)行解析,調(diào)用Next方法的時(shí)候進(jìn)行實(shí)例化并緩存。
優(yōu)點(diǎn)
使用Java SPI機(jī)制的優(yōu)勢是實(shí)現(xiàn)解耦,使得第三方服務(wù)模塊的裝配控制的邏輯與調(diào)用者的業(yè)務(wù)代碼分離,而不是耦合在一起。應(yīng)用程序可以根據(jù)實(shí)際業(yè)務(wù)情況啟用框架擴(kuò)展或替代框架組件。
缺點(diǎn)
雖然ServiceLoader也算是使用的延遲加載,但是基本只能通過遍歷全部獲取,也就是接口的實(shí)現(xiàn)類全部加載并實(shí)例化一遍。如果你并不想用某些實(shí)現(xiàn)類,它也被加載并實(shí)例化了,這就造成了浪費(fèi)。獲取某個(gè)實(shí)現(xiàn)類的方式不夠靈活,只能通過Iterator形式獲取,不能根據(jù)某個(gè)參數(shù)來獲取對(duì)應(yīng)的實(shí)現(xiàn)類。
多個(gè)并發(fā)多線程使用ServiceLoader類的實(shí)例是不安全的。
Dubbo的SPI機(jī)制

從圖中可以看出,Dubbo進(jìn)行各個(gè)模塊的擴(kuò)展時(shí),是通過ExtensionLoader與擴(kuò)展點(diǎn)進(jìn)行關(guān)聯(lián)的。
在Dubbo中的擴(kuò)展點(diǎn)需要滿足以下幾個(gè)特點(diǎn):
- 擴(kuò)展點(diǎn)必須是Interface類型,必須被@SPI注釋
- 配置文件存儲(chǔ)在META-INF/services/ 和META-INF/dubbo/ 和META-INF/dubbo/internal,這些路徑下定義的文件名為擴(kuò)展點(diǎn)接口的全類名,文件中以鍵值對(duì)的形式配置擴(kuò)展點(diǎn)的擴(kuò)展實(shí)現(xiàn),這與JDk SPI的存儲(chǔ)形式有很大不同,所以在Dubbo中無法直接使用ServiceLoader, 而是使用ExtensionLoader,可用于載入Dubbo中的各種可配置組件,比如動(dòng)態(tài)代理方式(ProxyFactory)、負(fù)載均衡策略(LoadBalance)、RCP協(xié)議(Protocol)、攔截器(Filter)、容器類型(Container)、集群方式(Cluster)和注冊(cè)中心類型等。
在META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory 中定義的擴(kuò)展 :
adaptive = com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi = com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
spring = com.alibaba.dubbo.config.spring.extension.SpringExtensionFactor
在標(biāo)識(shí)擴(kuò)展點(diǎn)時(shí)會(huì)用到這幾個(gè)標(biāo)識(shí),@SPI 、 @Adaptive、 @Activate
@SPI (注解在類上):該注解標(biāo)識(shí)了接口是一個(gè)擴(kuò)展點(diǎn),屬性value用來指定默認(rèn)適配擴(kuò)展點(diǎn)的名稱。
@Activate(注解在類型和方法上):@Activate注解在擴(kuò)展點(diǎn)的實(shí)現(xiàn)類上,表示了一個(gè)擴(kuò)展類被獲取到的條件,符合條件就被獲取,不符合條件就不獲取,根據(jù)@Activate中的group、value屬性來過濾。
@Adaptive(注解在類型和方法上):如果注解在類上,這個(gè)類就是缺省的適配擴(kuò)展。注解在擴(kuò)展點(diǎn)Interface的方法上時(shí),dubbo會(huì)動(dòng)態(tài)的生成一個(gè)這個(gè)擴(kuò)展點(diǎn)的適配擴(kuò)展類(生成代碼,動(dòng)態(tài)編譯實(shí)例化Class),名稱為擴(kuò)展點(diǎn)Interface的簡單類名+$Adaptive,這樣做的目的是為了在運(yùn)行時(shí)去適配不同的擴(kuò)展實(shí)例,在運(yùn)行時(shí)通過傳入的URL類型的參數(shù)或者內(nèi)部含有獲取URL方法的參數(shù),從URL中獲取到要使用的擴(kuò)展類的名稱,再去根據(jù)名稱加載對(duì)應(yīng)的擴(kuò)展實(shí)例,用這個(gè)擴(kuò)展實(shí)例對(duì)象調(diào)用相同的方法。如果運(yùn)行時(shí)沒有適配到運(yùn)行的擴(kuò)展實(shí)例,那么就使用@SPI注解缺省指定的擴(kuò)展。通過這種方式就實(shí)現(xiàn)了運(yùn)行時(shí)去適配到對(duì)應(yīng)的擴(kuò)展。
我們隨機(jī)找一個(gè)源碼中定義的接口: Transporter
@SPI("netty")
public interface Transporter {
// 綁定一個(gè)服務(wù)器
@Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
Server bind(URL url, ChannelHandler handler) throws RemotingException;
// 連接一個(gè)服務(wù)器,即創(chuàng)建一個(gè)客戶端
@Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
Client connect(URL url, ChannelHandler handler) throws RemotingException;
}
ExtensionLoader會(huì)通過createAdaptiveExtensionClassCode方法動(dòng)態(tài)生成一個(gè)Transporter$Adaptive類,生成的代碼如下:
package com.alibaba.dubbo.remoting;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Transporter$Adaptive implements com.alibaba.dubbo.remoting.Transporter{
public com.alibaba.dubbo.remoting.Client connect(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.remoting.RemotingException {
//URL參數(shù)為空則拋出異常。
if (arg0 == null)
throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("client", url.getParameter("transporter", "netty"));
if(extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([client, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader
(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.connect(arg0, arg1);
}
public com.alibaba.dubbo.remoting.Server bind(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.remoting.RemotingException {
if (arg0 == null)
throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("server", url.getParameter("transporter", "netty"));
if(extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([server, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader
(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.bind(arg0, arg1);
}
}
這些代碼都是模板代碼,最核心的代碼只有一行,是為了去獲取指定名稱的擴(kuò)展實(shí)例對(duì)象。
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
擴(kuò)展加載器 ExtensionLoader
它控制著所有擴(kuò)展點(diǎn)的初始化、加載擴(kuò)展的過程。
ExtensionLoader中會(huì)存儲(chǔ)兩個(gè)靜態(tài)屬性,EXTENSION_LOADERS保存內(nèi)核開放的擴(kuò)展點(diǎn)對(duì)應(yīng)的ExtensionLoader實(shí)例對(duì)象;EXTENSION_INSTANCES保存了擴(kuò)展類型(Class)和擴(kuò)展類型的實(shí)例對(duì)象
private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);
//這是jdk的SPI擴(kuò)展機(jī)制中配置文件路徑,dubbo為了兼容jdk的SPI
private static final String SERVICES_DIRECTORY = "META-INF/services/";
//用于用戶自定義的擴(kuò)展實(shí)現(xiàn)配置文件存放路徑
private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
//用于dubbo內(nèi)部提供的擴(kuò)展實(shí)現(xiàn)配置文件存放路徑
private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");
//擴(kuò)展加載器集合,key為擴(kuò)展接口,例如Protocol等
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
//擴(kuò)展實(shí)現(xiàn)集合,key為擴(kuò)展實(shí)現(xiàn)類,value為擴(kuò)展對(duì)象
//例如key為Class<DubboProtocol>,value為DubboProtocol對(duì)象
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();
//擴(kuò)展接口,例如Protocol等
private final Class<?> type;
//對(duì)象工廠,獲得擴(kuò)展實(shí)現(xiàn)的實(shí)例,用于injectExtension方法中將擴(kuò)展實(shí)現(xiàn)類的實(shí)例注入到相關(guān)的依賴屬性。
//比如StubProxyFactoryWrapper類中有Protocol protocol屬性,就是通過set方法把Protocol的實(shí)現(xiàn)類實(shí)例賦值
private final ExtensionFactory objectFactory;
//以下提到的擴(kuò)展名就是在配置文件中的key值,類似于“dubbo”等
//緩存的擴(kuò)展名與擴(kuò)展類映射,和cachedClasses的key和value對(duì)換。
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
//緩存的擴(kuò)展實(shí)現(xiàn)類集合
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();
//擴(kuò)展名與加有@Activate的自動(dòng)激活類的映射
private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
//緩存的擴(kuò)展對(duì)象集合,key為擴(kuò)展名,value為擴(kuò)展對(duì)象
//例如Protocol擴(kuò)展,key為dubbo,value為DubboProcotol
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();
//緩存的自適應(yīng)( Adaptive )擴(kuò)展對(duì)象,例如例如AdaptiveExtensionFactory類的對(duì)象
private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
//緩存的自適應(yīng)擴(kuò)展對(duì)象的類,例如AdaptiveExtensionFactory類
private volatile Class<?> cachedAdaptiveClass = null;
//緩存的默認(rèn)擴(kuò)展名,就是@SPI中設(shè)置的值
private String cachedDefaultName;
//創(chuàng)建cachedAdaptiveInstance異常
private volatile Throwable createAdaptiveInstanceError;
//拓展Wrapper實(shí)現(xiàn)類集合
private Set<Class<?>> cachedWrapperClasses;
//拓展名與加載對(duì)應(yīng)拓展類發(fā)生的異常的映射
private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();
ExtensionLoader沒有提供public的構(gòu)造方法,有一個(gè)私有的構(gòu)造方法,獲取ExtensionLoader實(shí)例的工廠方法,但是提供了一個(gè)public static的getExtensionLoader。其public成員方法中有三個(gè)比較重要的方法:
getActiveExtension: 根據(jù)條件獲取當(dāng)前擴(kuò)展可自動(dòng)激活的實(shí)現(xiàn)
getExtension: 根據(jù)名稱獲取當(dāng)前擴(kuò)展的指定實(shí)現(xiàn)
getAdaptiveExtension: 獲取當(dāng)前擴(kuò)展的自適應(yīng)實(shí)現(xiàn)
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
@SPI
public interface ExtensionFactory {
<T> T getExtension(Class<T> type, String name);
}
從上可以看出ExtensionFactory也是一個(gè)擴(kuò)展點(diǎn),有兩個(gè)實(shí)現(xiàn)類:SpiExtensionFactory和AdaptiveExtensionFactory,實(shí)際上還有一個(gè)SpringExtensionFactory,不同的實(shí)現(xiàn)類可以用不同的方式來完成擴(kuò)展點(diǎn)實(shí)現(xiàn)的加載。如果要加載的擴(kuò)展點(diǎn)類型是ExtensionFactory,那么object設(shè)置為null。
默認(rèn)的ExtensionFactory實(shí)現(xiàn)中,AdaptiveExtensionFactory被@Adaptive注解注釋,也就是說它是ExtensionFactory對(duì)應(yīng)的自適應(yīng)擴(kuò)展實(shí)現(xiàn)(每個(gè)擴(kuò)展點(diǎn)最多只能有一個(gè)自適應(yīng)實(shí)現(xiàn),如果所有實(shí)現(xiàn)中沒有被@Adaptive注釋的,那么dubbo會(huì)動(dòng)態(tài)生成一個(gè)自適應(yīng)實(shí)現(xiàn)類)
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
//擴(kuò)展對(duì)象的集合,默認(rèn)的可以分為dubbo 的SPI中接口實(shí)現(xiàn)類對(duì)象或者Spring bean對(duì)象
private final List<ExtensionFactory> factories;
public AdaptiveExtensionFactory() {
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
//遍歷所有支持的擴(kuò)展名
for (String name : loader.getSupportedExtensions()) {
//擴(kuò)展對(duì)象加入到集合中
list.add(loader.getExtension(name));
}
//返回一個(gè)不可修改的集合
factories = Collections.unmodifiableList(list);
}
@Override
public <T> T getExtension(Class<T> type, String name) {
for (ExtensionFactory factory : factories) {
//通過擴(kuò)展接口和擴(kuò)展名獲得擴(kuò)展對(duì)象
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
}
上述代碼中調(diào)用到了ExtensionLoader類中的getSupportedExtensions方法,所以接下來再分析ExtensionLoader類。
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
//擴(kuò)展點(diǎn)接口為空,拋出異常
if (type == null)
throw new IllegalArgumentException("Extension type == null");
//判斷type是否是一個(gè)接口類
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
//判斷是否為可擴(kuò)展的接口
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
//從擴(kuò)展加載器集合中取出擴(kuò)展接口對(duì)應(yīng)的擴(kuò)展加載器
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
//如果為空,則創(chuàng)建該擴(kuò)展接口的擴(kuò)展加載器,并且添加到EXTENSION_LOADERS
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
public T getAdaptiveExtension() {
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if (createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
//創(chuàng)建適配器對(duì)象
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;
}
在ExtensionLoader的私有構(gòu)造方法中可以看出,在選擇ExtensionFactory的時(shí)候,并不是用getExtension(name)來獲取某個(gè)具體的實(shí)現(xiàn)類,而是調(diào)用getAdaptiveExtension來獲取一個(gè)自適應(yīng)的實(shí)現(xiàn)。
首先檢查緩存的adaptiveInstance是否存在,如果存在則直接使用,否則的話調(diào)用createAdaptiveExtension方法來創(chuàng)建新的adaptiveInstance并且緩存起來,也就是說對(duì)于某個(gè)擴(kuò)展點(diǎn),每次調(diào)用ExtensionLoader.getAdaptiveExtension獲取到的都是同一個(gè)實(shí)例。
在調(diào)用getAdaptiveExtensionClass中首先調(diào)用getExtensionClasses()
在getAdaptiveExtensionClass()中,調(diào)用getExtensionClasses()獲取擴(kuò)展實(shí)現(xiàn)類數(shù)組,并存放在cachedClasses屬性中。
再從getExtensionClasses()看,當(dāng)cachedClasses為空時(shí),調(diào)用loadExtensionClasses()
getExtensionClasses()會(huì)加載當(dāng)前Extension的所有實(shí)現(xiàn),如果有@Adaptive類型,則會(huì)賦值給cachedAdaptiveClass屬性緩存起來,如果沒有找到@Adaptive類型實(shí)現(xiàn),則動(dòng)態(tài)創(chuàng)建一個(gè)AdaptiveExtensionClass。
首先會(huì)獲取到該擴(kuò)展點(diǎn)類的注解中的值,獲取默認(rèn)值,然后從特定目錄下讀取配置文件中的信息,
最后通過loadClass,將有關(guān)類放到extensionClasses變量中
private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
//緩存的自適應(yīng)擴(kuò)展對(duì)象
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
private Map<String, Class<?>> loadExtensionClasses() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
//@SPI內(nèi)的默認(rèn)值
String value = defaultAnnotation.value();
if ((value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
//只允許有一個(gè)默認(rèn)值
if (names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
if (names.length == 1) cachedDefaultName = names[0];
}
}
//從配置文件中加載實(shí)現(xiàn)類數(shù)組
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadDirectory(extensionClasses, DUBBO_DIRECTORY);
loadDirectory(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) {
//拼接接口全限定名,得到完整的文件名
String fileName = dir + type.getName();
try {
Enumeration<java.net.URL> urls;
//獲取ExtensionLoader類信息
ClassLoader classLoader = findClassLoader();
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
//遍歷文件
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement();
loadResource(extensionClasses, classLoader, resourceURL);
}
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", description file: " + fileName + ").", t);
}
}
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
try {
String line;
while ((line = reader.readLine()) != null) {
//跳過被#注釋的內(nèi)容
final int ci = line.indexOf('#');
if (ci >= 0) line = line.substring(0, ci);
line = line.trim();
if (line.length() > 0) {
try {
String name = null;
int i = line.indexOf('=');
if (i > 0) {
//根據(jù)"="拆分key跟value
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
if (line.length() > 0) {
//加載擴(kuò)展類
loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
}
} catch (Throwable t) {
IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
exceptions.put(line, e);
}
}
}
} finally {
reader.close();
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", class file: " + resourceURL + ") in " + resourceURL, t);
}
}
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
//該類是否實(shí)現(xiàn)擴(kuò)展接口
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error when load extension class(interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + "is not subtype of interface.");
}
//判斷該類是否為擴(kuò)展接口的適配器
if (clazz.isAnnotationPresent(Adaptive.class)) {
if (cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (!cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException("More than 1 adaptive class found: "
+ cachedAdaptiveClass.getClass().getName()
+ ", " + clazz.getClass().getName());
}
} else if (isWrapperClass(clazz)) {
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} else {
//通過反射獲得構(gòu)造器對(duì)象
clazz.getConstructor();
//未配置擴(kuò)展名,自動(dòng)生成,例如DemoFilter為 demo,主要用于兼容java SPI的配置。
if (name == null || name.length() == 0) {
name = findAnnotationName(clazz);
if (name.length() == 0) {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
}
}
// 獲得擴(kuò)展名,可以是數(shù)組,有多個(gè)拓?cái)U(kuò)展名。
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
Activate activate = clazz.getAnnotation(Activate.class);
//如果是自動(dòng)激活的實(shí)現(xiàn)類,則加入到緩存
if (activate != null) {
cachedActivates.put(names[0], activate);
}
for (String n : names) {
if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
//緩存擴(kuò)展實(shí)現(xiàn)類
Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
} else if (c != clazz) {
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
}
}
}
}
}
上述代碼完成了自適應(yīng)擴(kuò)展點(diǎn)類型的實(shí)現(xiàn)和實(shí)例化,下面方法是擴(kuò)展點(diǎn)自動(dòng)注入的實(shí)現(xiàn),它會(huì)獲取處理當(dāng)前實(shí)例的所有set方法對(duì)應(yīng)的參數(shù)類型和property名稱,根據(jù)這兩個(gè)條件從ExtensionFactory中查詢,如果有返回?cái)U(kuò)展點(diǎn)實(shí)例,那么就進(jìn)行注入操作。
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
//反射獲得該類中所有的方法
for (Method method : instance.getClass().getMethods()) {
//如果是set方法
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
/**
* Check {@link DisableInject} to see if we need auto injection for this property
*/
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
Class<?> pt = method.getParameterTypes()[0];
try {
//獲得屬性,比如StubProxyFactoryWrapper類中有Protocol protocol屬性,
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
//獲得屬性值,比如Protocol對(duì)象,也可能是Bean對(duì)象
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
//注入依賴屬性
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("fail to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
文章參考:
dubbo源碼一:ExtensionLoader及獲取適配類過程解析
Dubbo擴(kuò)展點(diǎn)加載機(jī)制 - ExtensionLoader
【Dubbo】Adaptive
