什么是 SPI ?
SPI(Service Provider Interface) 是一種服務(wù)發(fā)現(xiàn)機制, 主要原理是在運行時根據(jù)具體參數(shù)去查找約定路徑(JDK 默認(rèn)是在/META-INF/services/)下的配置實現(xiàn)類信息划乖。 通過然后類加載機制(ClassLoader)實現(xiàn)對類信息的加載以及后面的實例化浅妆,實現(xiàn)黑盒擴展的作用语盈。簡單點來說就是在運行時動態(tài)指定并加載實現(xiàn)類只酥,實現(xiàn)指定功能點擴展涨椒。
推薦連接: 高級開發(fā)必須理解的Java中SPI機制
Dubbo SPI 和JDK SPI 的區(qū)別
是的汪厨,Dubbo并沒有沿用JDK內(nèi)置的SPI機制包竹。 而是自行實現(xiàn)了一套SPI機制厉碟,從Dubbo的官方描述來看,Dubbo是對JDK的SPI進行了一次改進鼎姊。 那這里就有必要說明下巩那。JDK SPI有哪些問題呢? Dubbo 又是如何對其進行改進的呢此蜈?
JDK SPI問題
- 需要遍歷所有的實現(xiàn),并實例化噪生,然后我們在循環(huán)中才能找到我們需要的實現(xiàn)裆赵。
- 配置文件中只是簡單的列出了所有的擴展實現(xiàn),而沒有給他們命名跺嗽。導(dǎo)致在程序中很難去準(zhǔn)確的引用它們战授。
- 擴展如果依賴其他的擴展,做不到自動注入和裝配
- 不提供類似于Spring的IOC和AOP功能
- 擴展很難和其他的框架集成桨嫁,比如擴展里面依賴了一個Spring bean植兰,原生的Java SPI不支持
Dubbo SPI的增強
- 通過Map緩存的機制, 對Class類進行緩存璃吧,在運行時調(diào)用明確名稱的類楣导。 獲取對應(yīng)的類信息,進行實例化畜挨。 提高了啟動性能
- Dubbo SPI 實現(xiàn)了類似IOC 和AOP 的機制筒繁。提供了查找和動態(tài)擴展的功能噩凹。
- Dubbo 也提供了從擴展點容器獲取實例對象的功能, 如通過SpringExtensionFactory就實現(xiàn)了通過Spring的容器獲取依賴的功能。 可以更好的和其他框架進行集成毡咏。
Dubbo SPI示例
代碼示例
@SPI
public interface Car {
void test();
}
//寶馬
public class BWMCar implements Car {
@Override
public void test() {
System.out.println("hi驮宴, 我是寶馬車");
}
}
public static void main(String[] args) {
ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
Car car = extensionLoader.getExtension("bwm");
car.test();
}
配置示例
在/META-INF/dubbo/目錄下創(chuàng)建文件com.wgt.dubbo.samples.spi.demo1.car.Car文件,配置信息如下
bwm=com.wgt.dubbo.samples.spi.demo1.car.BWMCar
輸出結(jié)果
hi呕缭, 我是寶馬車
從示例中的來看堵泽。 流程時先ExtensionLoader.getExtensionLoader(Class class) 獲取到一個ExtensionLoader示例, 然后調(diào)用ExtensionLoader的getExtension(String name)方法獲取到目標(biāo)實例, 那么我們從ExtensionLoader.getExtensionLoader(Car.class)開始恢总,進入源碼迎罗,弄清原理。
代碼的示例很簡單离熏, 但是里面的學(xué)問有很多佳谦, 不過不需要心急,我們需要確弊檀粒看源碼的過程中不會迷失钻蔑。 接下來的源碼解析將圍繞著本段示例代碼來進行步步深入分析, 盡量做到細(xì)微入致奸鸯。
Dubbo 加載拓展點
- 創(chuàng)建ExtensionLoader實例源碼
@SuppressWarnings("unchecked")
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
}
// 判斷type 是否為接口
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
}
// 判斷是否標(biāo)注了@SPI注解
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type (" + type +
") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
}
// 從緩存中獲取ExtensionLoader咪笑。
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
//緩存中沒有的話 則創(chuàng)建一個新的ExtensionLoader加入緩存中。
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
//構(gòu)造方法
private ExtensionLoader(Class<?> type) {
this.type = type;
//此處默認(rèn)的objectFactory 為org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory娄涩,
// 該處的objectFactory 相當(dāng)于一個IOC容器窗怒。 后面在IOC部分詳細(xì)說明
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
從中可以看出創(chuàng)建ExtensionLoader實例的源碼是比較簡單的。 疑問點objectFactory對象工廠我們放到后面再說蓄拣。接下來我們先看getExtension方法都做了什么扬虚。
- getExtension方法源碼分析
@SuppressWarnings("unchecked")
public T getExtension(String name) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
if ("true".equals(name)) {
return getDefaultExtension();
}
// 緩存中根據(jù)name 獲取加載過的實例
final Holder<Object> holder = getOrCreateHolder(name);
Object instance = holder.get();
// double check synchronized 確保線程安全
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
// 創(chuàng)建新的Extension 信息
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
// 創(chuàng)建新的實例對象
@SuppressWarnings("unchecked")
private T createExtension(String name) {
// 此處會讀取/META-INF/services/ 下的擴展點文件信息。 將所有類信息加載進來后根據(jù)name 獲取具體的實例對象
// 當(dāng)前示例主要說明點球恤。
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
// 根據(jù)Class 信息通過反射進行實例化辜昵。
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// 進行依賴注入 在IOC 內(nèi)容詳細(xì)說明
injectExtension(instance);
// AOP 內(nèi)容。 使用裝飾器模式實現(xiàn) 在AOP部分詳細(xì)說明
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
// 調(diào)用instance 的初始化方法咽斧。 如果class 實現(xiàn)了Lifecycle 接口的話
initExtension(instance);
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
此處我們可以看出獲取擴展點實例的大致流程如下:
加載接口類型的所有實現(xiàn)類Class信息并緩存 ----> 根據(jù)name去獲取指定擴展實現(xiàn)類Class ----> 通過反射機制對實現(xiàn)類Class進行實例化并緩存 ----> 基于dubbo 的IOC機制對實例對象進行依賴注入----> 判斷是否有包裝類(wrapperClasses)堪置,決定是否要生成代理對象 ----> 最后對對象進行初始化之后返回該實例對象
- 第一步實現(xiàn)類的加載 getExtensionClasses()
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
// 加載所有class
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
private Map<String, Class<?>> loadExtensionClasses() {
// 通過@SPI("DefaultExtensionName") 緩存默認(rèn)的extension name
cacheDefaultExtensionName();
//根據(jù)type.name 查找各個配置文件路徑下的實現(xiàn)類拓展配置
Map<String, Class<?>> extensionClasses = new HashMap<>();
// internal extension load from ExtensionLoader's ClassLoader first
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName(), true);
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"), true);
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
return extensionClasses;
}
private void loadDirectory(Map<String, Class<?>> extensionClasses,
String dir,
String type,
boolean extensionLoaderClassLoaderFirst) {
String fileName = dir + type;
try {
Enumeration<java.net.URL> urls = null;
// 獲取ClassLoader
ClassLoader classLoader = findClassLoader();
// try to load from ExtensionLoader's ClassLoader first
// 如果需要先嘗試使用ExtensionLoader 查找文件資源
if (extensionLoaderClassLoaderFirst) {
ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
urls = extensionLoaderClassLoader.getResources(fileName);
}
}
// 使用默認(rèn)加載器獲取資源文件
if(urls == null || !urls.hasMoreElements()) {
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 occurred when loading extension class (interface: " +
type + ", description file: " + fileName + ").", t);
}
}
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
//.... 此處省略文件解析部分代碼张惹,不在主邏輯范疇舀锨,有興趣的同學(xué)可以自行深入
// 遍歷加載class實現(xiàn)類, 通過Class.forName(line, true, classLoader) 實現(xiàn)類加載
loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
//.... 此處省略異常處理
}
//解析類信息
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error occurred when loading extension class (interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + " is not subtype of interface.");
}
// 緩存標(biāo)注了@Adaptive的擴展實現(xiàn)類宛逗, 具體作用后面自適應(yīng)問題詳細(xì)說明
if (clazz.isAnnotationPresent(Adaptive.class)) {
cacheAdaptiveClass(clazz);
// 判斷是否為包裝類坎匿, 緩存包裝類
} else if (isWrapperClass(clazz)) {
cacheWrapperClass(clazz);
} else {
clazz.getConstructor();
// 如果沒有指定name 生成name
if (StringUtils.isEmpty(name)) {
name = findAnnotationName(clazz);
if (name.length() == 0) {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
}
}
// 分割name, 因為可以多個name 指向同一個實現(xiàn)類。例: aa, bb = org.apache.dubbo.extension.C
String[] names = NAME_SEPARATOR.split(name);
if (ArrayUtils.isNotEmpty(names)) {
// 緩存@Activate標(biāo)注的類信息
cacheActivateClass(clazz, names[0]);
for (String n : names) {
// 遍歷緩存 name和class 信息
cacheName(clazz, n);
saveInExtensionClass(extensionClasses, clazz, n);
}
}
}
}
到此基本了解了拓展類的加載過程碑诉, 基本邏輯也非常簡單彪腔。 獲取到classLoader并調(diào)用Class.forname("className")將擴展類信息加載進JVM中, 之后就是從功能擴展的維度进栽,對class進行不同緯度的緩存德挣, 方便后面在某項功能點中快速獲取類信息。
Dubbo IOC實現(xiàn)
-
基于IOC的依賴注入 injectExtension(instance)
private T injectExtension(T instance) {
if (objectFactory == null) {
return instance;
}
try {
for (Method method : instance.getClass().getMethods()) {
if (!isSetter(method)) {
continue;
}
/**
* 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];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
String property = getSetterProperty(method);
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("Failed to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
從上面的邏輯很簡單的看出大致邏輯就是先獲取所有的setter方法快毛,獲取setter方法的參數(shù)的類型和名稱格嗅, 最后通過我們前面看到過的objectFactory獲取到依賴對象, 然后對其進行依賴注入唠帝。 那這里我們就需要深扒一下這個objectFactory 到底是個神馬屯掖。前面我在objectfactory的注釋說明了默認(rèn)為AdaptiveExtensionFactory類, 我們先來看下他的源碼
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
// 保存了ExtensionFactory的實現(xiàn)類襟衰。
private final List<ExtensionFactory> factories;
public AdaptiveExtensionFactory() {
// 初始化將 其他ExtensionFactory拓展類加載緩存
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
for (String name : loader.getSupportedExtensions()) {
list.add(loader.getExtension(name));
}
factories = Collections.unmodifiableList(list);
}
@Override
public <T> T getExtension(Class<T> type, String name) {
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
}
很明顯該類是一個策略類贴铜, 啟動時候會講其他的ExtensionFactory實現(xiàn)類加載進來, 然后再獲取Extension實現(xiàn)的時候直接遍歷各個容器來進行查找瀑晒。那我們來看下ExtensionFactory默認(rèn)實現(xiàn)的子類绍坝。
// SPI 默認(rèn)的IOC實現(xiàn),
public class SpiExtensionFactory implements ExtensionFactory {
@Override
public <T> T getExtension(Class<T> type, String name) {
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
if (!loader.getSupportedExtensions().isEmpty()) {
return loader.getAdaptiveExtension();
}
}
return null;
}
}
// Spring 實現(xiàn)
public class SpringExtensionFactory implements ExtensionFactory {
private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class);
private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>();
public static void addApplicationContext(ApplicationContext context) {
CONTEXTS.add(context);
if (context instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) context).registerShutdownHook();
}
}
public static void removeApplicationContext(ApplicationContext context) {
CONTEXTS.remove(context);
}
public static Set<ApplicationContext> getContexts() {
return CONTEXTS;
}
// currently for test purpose
public static void clearContexts() {
CONTEXTS.clear();
}
@Override
@SuppressWarnings("unchecked")
public <T> T getExtension(Class<T> type, String name) {
//SPI should be get from SpiExtensionFactory
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
return null;
}
for (ApplicationContext context : CONTEXTS) {
T bean = BeanFactoryUtils.getOptionalBean(context, name, type);
if (bean != null) {
return bean;
}
}
logger.warn("No spring extension (bean) named:" + name + ", try to find an extension (bean) of type " + type.getName());
return null;
}
}
以上ExtensionFactory主要分為兩類
SpiExtensionFactory是Dubbo 的IOC默認(rèn)容器苔悦,對象是從前面加載緩存中獲取的SPI的擴展對象轩褐。 這是Dubbo 的默認(rèn)機制
SpringExtensionFactory是使用Spring的IOC容器。我們可以將Spring容器中的Bean注入到Extension實例中玖详,通過該容器我們可以更好的繼承Spring框架甚至其他組件把介。
Dubbo Aop實現(xiàn)
可能用過一些容器框架的同學(xué)都知道AOP的基本原理。 甚至有的同學(xué)可能首先想到的就是動態(tài)代理模式蟋座,這并沒有錯拗踢。 但是方法不是唯一的, Dubbo中獲取的代理對象是通過裝飾器模式實現(xiàn)的向臀,接下來我們繼續(xù)深入看下源碼秒拔。
通過前面創(chuàng)建Extension我們可以知道, 是否需要生成對實力對象進行封裝的依據(jù)是cachedWrapperClasses是否為空飒硅。同時通過類信息加載過程可以知道,在加載類的時候會對cachedWrapperClasses進行緩存作谚, 那我們這邊舉例一個wrapper類三娩,然后再次運行。
- CarWrapper
public class CarWrapper implements Car{
private Car car;
public CarWrapper(Car car){
this.car = car;
}
@Override
public void test(CarBrand brand) {
System.out.println("包裝類前置執(zhí)行");
car.test(brand);
System.out.println("包裝類后置執(zhí)行");
}
}
- 配置信息
bwm=com.wgt.dubbo.samples.spi.demo1.car.BWMCar
audi=com.wgt.dubbo.samples.spi.demo1.car.AudiCar
# 代理類配置
com.wgt.dubbo.samples.spi.demo1.car.CarWrapper
- 控制臺輸出
包裝類前置執(zhí)行
hi妹懒, 我是寶馬車
包裝類后置執(zhí)行
從輸出結(jié)果來看雀监, 很明顯在我根據(jù)(name="bwm"), 去獲取Extension實例的時候,實際獲取到的是CarWrapper類会前, 只不過BWMCar是作為一個代理對象傳入到CarWrapper中好乐。
- 源碼參考
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
// 獲取CarWrapper類的有參構(gòu)造起, 并將instance作為構(gòu)造參數(shù)返回新的包裝類瓦宜。
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
當(dāng)前方式可以獲取到包裝后的實例對象蔚万, 但是此種方法還是不夠靈活。 Dubbo 中還有一種自適應(yīng)的機制临庇, 可以根據(jù)請求參數(shù)動態(tài)的獲取拓展類進行調(diào)用反璃, 通過策略+裝飾器的模式實現(xiàn)更加完善的代理
Dubbo 自適應(yīng)機制
第一次聽到自適應(yīng)機制難免有些陌生, 擴展的自適應(yīng)實例其實就是一個Extension的代理假夺,它實現(xiàn)了擴展點接口淮蜈。在調(diào)用擴展點的接口方法時,會根據(jù)實際的參數(shù)來決定要使用哪個擴展已卷。
-
@Adaptive
@Adaptive注解是作用在做自適應(yīng)擴展點的注解類梧田, 可以作用在類上和方法上
- 當(dāng)@Adaptive標(biāo)注在類上時,在調(diào)用getAdaptiveExtension方法時侧蘸,直接返回該類裁眯,表示代理類由手工實現(xiàn),并不需要Dubbo自動生成實現(xiàn)類闺魏。 可以參考 AdaptiveCompiler 和 AdaptiveExtensionFactory 實現(xiàn)
- 當(dāng)@Adaptive標(biāo)注在接口的方法上時未状, 表明調(diào)用該方法可以通過URL參數(shù)動態(tài)調(diào)用擴展實現(xiàn)類的對應(yīng)方法。
- 被@Adaptive修飾得方法得參數(shù) 必須滿足參數(shù)中有一個是URL類型析桥,或者有至少一個參數(shù)有一個公共的返回URL的get方法
- 在調(diào)用動態(tài)生成代理類的非@Adaptive方法是司草, 默認(rèn)會拋出UnsupportedOperationException異常
-
代碼示例
說明: @Adaptive標(biāo)注在類上的邏輯相當(dāng)簡單。 這里就不贅述了泡仗。 直接以標(biāo)注在方法上的例子為例
@SPI public interface Car { //動態(tài)生成代理類接口 @Adaptive void test(CarBrand brand); } //自適應(yīng)節(jié)點埋虹, 獲取org.apache.dubbo.common.URL參數(shù)的接口 public interface CustomerAdaptiveNode { URL getUrl(); } //車品牌 public interface CarBrand extends CustomerAdaptiveNode { CarBrand BWM = getCarBrand("bwm"); CarBrand AUDI = getCarBrand("audi"); CarBrand BENZ = getCarBrand("benz"); static CarBrand getCarBrand(String brandName){ return new CarBrand() { @Override public URL getUrl() { URL url = new URL(null, null, 0); url = url.addParameter("car", brandName); return url; } }; } } //寶馬車 public class BWMCar implements Car { @Override public void test(CarBrand brand) { System.out.println("hi, 我是寶馬車"); } } //..... 此處省略 奔馳車和奧迪車源碼 //Main方法 public static void main(String[] args) { ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class); //Car car = extensionLoader.getExtension("bwm"); // 獲取自適應(yīng)代理實例 Car car = extensionLoader.getAdaptiveExtension(); //通過車品牌調(diào)用接口 car.test(CarBrand.BENZ); car.test(CarBrand.BWM); car.test(CarBrand.AUDI); }
- 控制臺輸出
hi, 我是奔馳車
hi娩怎,我是寶馬車
hi, 我是奧迪車
可以看到搔课,我們在調(diào)用同一個實例的同一個方法, 我們根據(jù)不同的CarBrand 參數(shù)實現(xiàn)了自適應(yīng)的去調(diào)用對應(yīng)擴展實例的方法截亦。
-
源碼參考
public T getAdaptiveExtension() { // 一如既往的緩存套路 Object instance = cachedAdaptiveInstance.get(); if (instance == null) { if (createAdaptiveInstanceError != null) { throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); } synchronized (cachedAdaptiveInstance) { instance = cachedAdaptiveInstance.get(); if (instance == null) { try { // 創(chuàng)建自適應(yīng)實例 instance = createAdaptiveExtension(); cachedAdaptiveInstance.set(instance); } catch (Throwable t) { createAdaptiveInstanceError = t; throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t); } } } } return (T) instance; } private T createAdaptiveExtension() { try { // 獲取自適應(yīng)擴展類Class爬泥, 調(diào)用newInstance方法進行實例化,然后進行注入 return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e); } } private Class<?> getAdaptiveExtensionClass() { // 眼熟的初始加載加載擴展累信息 getExtensionClasses(); // 判斷是否有標(biāo)注在類上的@Adaptive 擴展類 if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } //動態(tài)生成自適應(yīng)擴展累 return cachedAdaptiveClass = createAdaptiveExtensionClass(); } private Class<?> createAdaptiveExtensionClass() { // 自適應(yīng)擴展累代碼生成器 生成源代碼崩瓤, // 此處不在深入生成代碼原理袍啡, 有興趣的小伙伴自行研究, 下面會貼出生成好的代碼 String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate(); //獲取classLoader ClassLoader classLoader = findClassLoader(); // 獲取編譯器 org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); // 將源代碼進行編譯生成Class 對象 return compiler.compile(code, classLoader); }
-
自適應(yīng)擴展累源碼
package com.wgt.dubbo.samples.spi.demo1.car; import org.apache.dubbo.common.extension.ExtensionLoader; public class Car$Adaptive implements com.wgt.dubbo.samples.spi.demo1.car.Car { public void test(com.wgt.dubbo.samples.spi.demo1.brand.CarBrand arg0) { if (arg0 == null) throw new IllegalArgumentException("com.wgt.dubbo.samples.spi.demo1.brand.CarBrand argument == null"); if (arg0.getUrl() == null) throw new IllegalArgumentException("com.wgt.dubbo.samples.spi.demo1.brand.CarBrand argument getUrl() == null"); org.apache.dubbo.common.URL url = arg0.getUrl(); String extName = url.getParameter("car", "bwm"); if (extName == null) throw new IllegalStateException("Failed to get extension (com.wgt.dubbo.samples.spi.demo1.car.Car) name from url (" + url.toString() + ") use keys([car])"); com.wgt.dubbo.samples.spi.demo1.car.Car extension = (com.wgt.dubbo.samples.spi.demo1.car.Car) ExtensionLoader.getExtensionLoader(com.wgt.dubbo.samples.spi.demo1.car.Car.class).getExtension(extName); extension.test(arg0); } }
從以上的源代碼可以看出却桶, 所謂的根據(jù)參數(shù)自適應(yīng)境输,其實就是通過URL對象中的parameter 參數(shù)去獲取對應(yīng)的擴展實現(xiàn)蔗牡。 獲取的擴展的方式同樣是通過策略模式進行實現(xiàn)。 不過此種方式可以確保在運行時才能確定具體執(zhí)行的擴展累嗅剖,對于SPI機制的靈活性來說非常的有意義的辩越。
Dubbo @Activate
? 相較于前面的內(nèi)容,都是根據(jù)一些特定參數(shù)獲取具體的擴展類信粮, 但其實除此之外黔攒, 我們還會有同時用到多個擴展類,最常見的就是Dubbo 的Filter 機制蒋院,當(dāng)我們需要在執(zhí)行rpc調(diào)用的前后做一個操作時亏钩, 我們就可以通過實現(xiàn)Filter接口,并將其配置到org.apache.dubbo.rpc.Filter下欺旧。 但是我們在一次rpc調(diào)用時可能會有多個filter姑丑, 那么如何決定每個filter 的作用域,以及執(zhí)行順序呢辞友?
此時@Activate的作用的出現(xiàn)了栅哀,我們可以通過group, 和value 兩者來決定是否此次的調(diào)用啟用對應(yīng)攔截器称龙,并通過order 排序決定執(zhí)行順序留拾。
- 源碼參考
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
/**
* 根據(jù)分組
* 例如
* CommonConstants.PROVIDER
* CommonConstants.CONSUMER
* @return
*/
String[] group() default {};
/**
* 根據(jù)org.apache.dubbo.common.URL 中的parameters 參數(shù)決定是否啟用
* @return
*/
String[] value() default {};
@Deprecated
String[] before() default {};
@Deprecated
String[] after() default {};
/**
* Filter 順序
* @return
*/
int order() default 0;
}
- 攔截器獲取源碼邏輯
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> activateExtensions = new ArrayList<>();
List<String> names = values == null ? new ArrayList<>(0) : asList(values);
if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
getExtensionClasses();
for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
String name = entry.getKey();
Object activate = entry.getValue();
String[] activateGroup, activateValue;
if (activate instanceof Activate) {
activateGroup = ((Activate) activate).group();
activateValue = ((Activate) activate).value();
} else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) {
activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group();
activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value();
} else {
continue;
}
if (isMatchGroup(group, activateGroup)
&& !names.contains(name)
&& !names.contains(REMOVE_VALUE_PREFIX + name)
&& isActive(activateValue, url)) {
activateExtensions.add(getExtension(name));
}
}
activateExtensions.sort(ActivateComparator.COMPARATOR);
}
List<T> loadedExtensions = new ArrayList<>();
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if (!name.startsWith(REMOVE_VALUE_PREFIX)
&& !names.contains(REMOVE_VALUE_PREFIX + name)) {
if (DEFAULT_KEY.equals(name)) {
if (!loadedExtensions.isEmpty()) {
activateExtensions.addAll(0, loadedExtensions);
loadedExtensions.clear();
}
} else {
loadedExtensions.add(getExtension(name));
}
}
}
if (!loadedExtensions.isEmpty()) {
activateExtensions.addAll(loadedExtensions);
}
return activateExtensions;
}