日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Dubbo源码学习--环境搭建及基础准备(ServiceLoader、ExtensionLoader)

發布時間:2024/8/24 编程问答 30 如意码农
生活随笔 收集整理的這篇文章主要介紹了 Dubbo源码学习--环境搭建及基础准备(ServiceLoader、ExtensionLoader) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

環境搭建

  1. Github上下載Dubbo最新發布版本,樓主下載版本為2.5.7。
  2. cd到源碼解壓目錄,maven編譯,命令為:
mvn clean install -Dmaven.test.skip
  1. 生成Intellij idea相關配置文件,命令為:
mvn idea:idea
  1. 雙擊運行生成的dubbo-parent.ipr文件

Java SPI

SPI是Service Provider Interfaces的簡稱,是Java中定義的一個很重要的規范,SPI使得應用之間變得更靈活、程序間更解耦。

一般在應用中會定義一個接口,具體的實現由對應的實現類去完成,即服務提供者(Service Provider)。模塊與模塊之間基于接口編程,模塊之間不能對實現類進行硬編碼、不能在代碼里寫具體的實現類,否則就違反了“可插拔原則”,如果要替換一種實現,就需要修改代碼。此時,SPI提供了一種服務發現機制,完美解決了這個問題。

SPI機制基本思路是通過JDK提供的java.util.ServiceLoader類去主動發現服務,不需要硬編碼具體的類。

當服務接口有多個實現類(即服務提供者)時,在jar包的META-INF/services/目錄下創建一個以服務接口命名的文件,文件內容是該服務接口的具體實現類的全類名,一行記錄是一個實現類的全類名。當外部程序裝配這個模塊時,通過jar包的META-INF/services/目錄里的配置文件就可以找到具體的實現類名,從而進行實例化、完成模塊的注入。

Java SPI 示例

定義服務接口:

package jdkspi;

public interface WorkerService {
void work();
}

該服務接口的兩個實現類如下:

package jdkspi.impl;

import jdkspi.WorkerService;

public class WorkerServiceA implements WorkerService {

    public void work() {
System.out.println("work hard ......");
}
}
package jdkspi.impl;

import jdkspi.WorkerService;

public class WorkerServiceB implements WorkerService {

    public void work() {
System.out.println("work lazy ......");
}
}

在resources下新建目錄META-INF/services/,在目錄下新建文件。文件名為服務接口全名jdkspi.WorkerService,具體內容如下

jdkspi.impl.WorkerServiceA      //服務接口實現類全名
jdkspi.impl.WorkerServiceB //服務接口實現類全名

測試示例執行入口:

package jdkspi;

import java.util.Iterator;
import java.util.ServiceLoader; public class Test { public static void main(String[] args) {
ServiceLoader<WorkerService> serviceLoader = ServiceLoader.load(WorkerService.class);
WorkerService service = null;
Iterator<WorkerService> iterator = serviceLoader.iterator(); while (iterator.hasNext()) {
service = iterator.next();
service.work();
}
}
}

執行測試示例后,結果如下:

work hard ......
work lazy ...... Process finished with exit code 0

ServiceLoader源碼分析

ServiceLoader是一個final類,不能被繼承,實現了Iterable接口,可以遍歷,如下:

public final class ServiceLoader<S> implements Iterable<S>

ServiceLoader屬性如下:

    private static final String PREFIX = "META-INF/services/";

    // The class or interface representing the service being loaded
private final Class<S> service; // The class loader used to locate, load, and instantiate providers
private final ClassLoader loader; // The access control context taken when the ServiceLoader is created
private final AccessControlContext acc; // Cached providers, in instantiation order
private LinkedHashMap<String,S> providers = new LinkedHashMap<>(); // The current lazy-lookup iterator
private LazyIterator lookupIterator;
  • PREFIX: 定義了配置文件的路徑,是一個final類型常量,不能設置不能更改。表面Java SPI配置文件默認放在META-INF/services/路徑下
  • service: 定義服務接口類,final類型變量,一旦被賦值便不能修改,由load方法傳入
  • loader: 類加載器,一旦被賦值便不能修改
  • acc: 訪問控制上下文,一旦被賦值比昂不修改
  • providers: 存儲服務提供者,也即具體實現類。存儲的順序為配置文件中實現類的排列先后順序
  • lookupIterator: 迭代器,實現延遲加載的效果

ServiceLoader只有一個構造器,且是內部構造器。不能再外部直接通過new命令創建實例對象。如下:

    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();
}

ServiceLoader提供了三種靜態類方法來創建實例對象。如下:

    public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) {
return new ServiceLoader<>(service, loader);
} public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
} 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);
}
  • load(Class<S> service): 利用當前線程持有的ClassLoader創建實例
  • load(Class<S> service, ClassLoader loader): 利用指定的ClassLoader創建實例
  • loadInstalled(Class<S> service): 利用系統頂級ClassLoader創建實例

ServiceLoader提供iterator()方法用以生成迭代器。迭代器中方法內部具體由lookupIterator實現。如下:

    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();
} };
}

lookupIterator是LazyIterator的對象實例,LazyIterator是一個內部類,實現了Iterator接口,源碼如下:

    private class LazyIterator
implements Iterator<S>
{ Class<S> service;
ClassLoader loader;
Enumeration<URL> configs = null;
Iterator<String> pending = null;
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();
} }

從上面源碼中,不難發現:服務提供者的實例化過程是在具體調用時進行的,延遲加載。

Java SPI機制的ServiceLoader缺點:

  1. 每次獲取一個實現類都必須遍歷加載所有的實現類,即使是不想使用的實現類也加載了,造成了資源的浪費。
  2. 不能定向獲取對應的實現類,必須iterator遍歷查找,比較慢

Dubbo拓展機制

Dubbo拓展機制應用的就是Java SPI的思想。Java SPI配置文件中一條記錄是一個實現類全名,但Dubbo配置文件中存儲的是key-value鍵值對,value存儲的是實現類全名。示例如下:

ls=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.ListTelnetHandler
ps=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.PortTelnetHandler
cd=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.ChangeTelnetHandler
pwd=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.CurrentTelnetHandler
invoke=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.InvokeTelnetHandler
trace=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.TraceTelnetHandler
count=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.CountTelnetHandler

類似Java SPI機制的ServiceLoader,Dubbo中也有一個拓展加載器ExtensionLoader。ExtensionLoader中定義了配置文件的存儲路徑:

    private static final String SERVICES_DIRECTORY = "META-INF/services/";

    private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";

    private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";

ExtensionLoader構造器也是內部構造器,在外部不能直接通過new命令來創建對象實例:

    private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

同樣,ExtensionLoader提供靜態類方法getExtensionLoader來生成實例:

    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null)
throw new IllegalArgumentException("Extension type == null");
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
} ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}

調用getExtension方法可以根據name值獲取指定的拓展實現類,實例化后的拓展實現類以Holder類封裝存儲在cachedInstances中,cachedInstances是ConcurrentMap<String, Holder<Object>>變量。

    /**
* 返回指定名字的擴展。如果指定名字的擴展不存在,則拋異常 {@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;
}

getExtension方法中采用了double-check機制,拓展實現類的實例化是在createExtension方法中完成的:

    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);
}
}

方法實現大體流程為:

  1. name作為key值,獲取對應的class。在getExtensionClasses中是有做同步處理的
  2. 根據得到的class創建實例
  3. 對實例化對象進行依賴注入
  4. 對依賴注入后的實例化對象進行包裝

依賴注入及包裝源碼如下:

    private T injectExtension(T instance) {
try {
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
Class<?> pt = method.getParameterTypes()[0];
try {
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
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拓展機制的大體流程,跟Java SPI機制非常類似,可看作Java SPI機制的一個優化與拓展。下一節將探討provider服務的發布過程

總結

以上是生活随笔為你收集整理的Dubbo源码学习--环境搭建及基础准备(ServiceLoader、ExtensionLoader)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚洲成人一二区 | 欧美高清免费 | a点w片 | 第色| 午夜嘿嘿嘿 | 中文字幕理论片 | 老司机久久| 夜夜爽av福利精品导航 | 狠狠干超碰| 日本一本久久 | 亚洲欧美日韩动漫 | 五月婷婷七月丁香 | 亚洲色图36p | 999视频在线 | 亚洲成av人片在线观看 | 久久资源av | 日本老少交| 国产一区二区在线不卡 | 色婷婷视频网 | 大学生三级中国dvd 日韩欧美一区二区区 | 99re最新| mm视频在线观看 | 91视频免费观看网站 | 99色99| 综合天天色 | 日本3级网站 | 国产精品午夜在线观看 | 久久久久久无码精品大片 | 久久久久久久久久国产 | v天堂在线 | 草民午夜理伦三级 | 免费av电影网站 | 人妻在线一区 | 国产综合内射日韩久 | 午夜啊啊啊 | 性欧美一级 | 国产中文字幕在线视频 | 青青草av在线播放 | 久久久久a | av影视天堂| 夜间福利在线观看 | 福利在线一区二区三区 | 国产激情视频一区二区 | 亚洲天堂av电影 | 日本中文在线播放 | 日日干天天| 97碰碰碰| 亚洲综合免费 | 美女大黄动图 | 日韩xx视频 | 好吊色欧美一区二区三区视频 | 黄色激情网站 | 成人av电影在线播放 | 91新网站| 亚洲av无码电影在线播放 | 国产精品一区视频 | 亚洲一区二区三区四区av | 久久黄网站| 亚洲va在线观看 | 国产欧美日韩中文字幕 | 亚洲射射| 无限资源日本好片 | 瑟瑟在线视频 | 夜夜夜撸 | 中文字幕人妻一区二区在线视频 | 亚洲图区欧美 | 成人精品区| 久久久久国产精品视频 | 欧美日韩久久久久 | 中出在线 | 欧美激情在线播放 | 久久婷综合 | 日本黄色免费 | 九色91porny| 日本黄在线 | 熊猫电影yy8y全部免费观看 | 在线精品视频播放 | 五月天最新网址 | 欧美第七页 | 成人做爰100 | 亚洲淫 | 粉嫩在线 | 精品国偷自产一区二区三区 | 有码视频在线观看 | 国产一区不卡视频 | 亚洲永久网站 | 亚洲人人爱| 午夜色网 | 91看片免费看 | 熟妇人妻无乱码中文字幕真矢织江 | 久久久久人妻一区二区三区 | 久草97| 午夜免费福利在线 | 国产午夜免费福利 | 亚洲三级在线视频 | 欧美天天 | 亚洲国产精品女人久久久 | 亚洲国产专区 | 欧美成人三级在线视频 |