Dubbo SPI 依賴注入
一、何爲依賴注入
如果作爲Java後端開發、一定是熟悉使用spring這個強大的IOC工具,依賴注入就一定是非常的瞭解的!在Dubbo自動生成SPI的擴展實例的時候也會發生依賴注入的場景,舉一個具體的例子。
1、例子
動態獲取配置中心,這裏getDynamicConfiguration()並沒有去處理設置 ZookeeperTransporter,怎麼處理進去的?
DynamicConfigurationFactory factories = ExtensionLoader
.getExtensionLoader(DynamicConfigurationFactory.class)
.getExtension(url.getProtocol());
DynamicConfiguration configuration = factories.getDynamicConfiguration(url);
2、DynamicConfigurationFactory 具體代碼
2.1 SPI 接口
/**
* 默認是什麼都沒有實現的
*/
@SPI("nop")
public interface DynamicConfigurationFactory {
/**
* 根據SPI動態獲取配置中心
* @param url
* @return
*/
DynamicConfiguration getDynamicConfiguration(URL url);
}
2.2 SPI的實現類
/**
* 動態配置中心 http://dubbo.apache.org/zh-cn/docs/user/configuration/config-center.html
*/
public class ZookeeperDynamicConfigurationFactory extends AbstractDynamicConfigurationFactory {
/**
* Zookeeper 也是動態生成的擴展
*/
private ZookeeperTransporter zookeeperTransporter;
public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) {
this.zookeeperTransporter = zookeeperTransporter;
}
@Override
protected DynamicConfiguration createDynamicConfiguration(URL url) {
return new ZookeeperDynamicConfiguration(url, zookeeperTransporter);
}
}
2.3 ZookeeperDynamicConfigurationFactory 的依賴也是一個SPI
@SPI("curator")
public interface ZookeeperTransporter {
@Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
ZookeeperClient connect(URL url);
}
3、注入詳情
根據之前的瞭解,獲取某個具體的擴展Class之後,反射完成成都需要進行依賴注入的。
org.apache.dubbo.common.extension.ExtensionLoader#injectExtension
獲取這個方法的第一個參數Class、獲取方法的名稱 去掉set 必須爲標準的,然後根據ExtensionFactory 擴展工廠進行獲取具體的實例的信息進行注入。
/**
* 信息 依賴注入:內部SPI ACTIVE 的依賴需要 ExtensionFactory 工廠能夠處理。
* @param instance
* @return
*/
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
if (isSetter(method)) {
/**
* 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 {
/**
* 進行依賴注入的時候方法的名稱必須和SPI中存在的名稱一致! 或者如果是Spring 中,必須是 bean 的名稱
*/
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;
}
二、Dubbo 依賴注入
一個SPI擴展對應一個ExtensionLoader,所有的ExtensionLoader都有ExtensionFactory依賴注入處理工廠,一個SPI對應一個Adaptive(可能是默認的、或者自定義的)
1、ExtensionFactory 怎麼獲取的
ExtensionLoader 是一個載體,每一個實例都有唯一的一個ExtensionFactory 依賴處理工廠,getAdaptiveExtension 證明這個是一個自適應的,可以根據參數變化而獲取不同的實現。
private ExtensionLoader(Class<?> type) {
this.type = type;
// 工廠擴展實例
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
1.1 ExtensionFactory 工廠
/**
* ExtensionFactory
*/
@SPI
public interface ExtensionFactory {
/**
* 獲取擴展實例工廠方法,比如SPi 或者Spring 等等
* Get extension.
*
* @param type object type.
* @param name object name.
* @return object instance.
*/
<T> T getExtension(Class<T> type, String name);
}
1.2 查看配置
META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionFactory
adaptive=org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=org.apache.dubbo.common.extension.factory.SpiExtensionFactory
1.3 自定義擴展,根據順序獲取擴展實例
這裏是自定義的一個擴展,根據當前工程中所有的ExtensionFactory實例,誰先獲取到一個就使用誰的!
/**
* AdaptiveExtensionFactory 依賴注入工廠
*/
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
private final List<ExtensionFactory> factories;
public AdaptiveExtensionFactory() {
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) {
// 依賴注入 根據第一匹配原則去查找,如果SPI中有 使用 SpiExtensionFactory,如果Spring 容器中有 使用Spring 容器中的!
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
}
1.4 SpiExtensionFactory 獲取到SPI的注入
獲取這個注入Class的是否爲SPI的實現,獲取到一個擴展的實例,然後根據參數動態的獲取到具體的實例。
/**
* SpiExtensionFactory 處理相關的依賴
*/
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);
//依賴注入必須實現 接口方法必須實現 Adaptive
if (!loader.getSupportedExtensions().isEmpty()) {
return loader.getAdaptiveExtension();
}
}
return null;
}
}
2、ZookeeperTransporter 怎麼獲取的?
injectExtension 這個方法,獲取到了方法參數org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter 這個class的名稱 and 方法的名稱
META-INF/dubbo/internal/org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter
curator=org.apache.dubbo.remoting.zookeeper.curator.CuratorZookeeperTransporter
有了Class,SpiExtensionFactory這個裏面獲取到SPI的信息一定能夠獲取得到具體的擴展類的實例,使用了默認的自定義擴展,根據URL進行獲取具體的實例。
@SPI("curator")
public interface ZookeeperTransporter {
@Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
ZookeeperClient connect(URL url);
}
默認的自定義擴展
2.1 AdaptiveExtensionFactory 依賴注入
2.2 獲取SPI的擴展實現類
2.3 返回擴展 ZookeeperTransporter$Adaptive
package org.apache.dubbo.remoting.zookeeper;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class ZookeeperTransporter$Adaptive implements org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter {
public org.apache.dubbo.remoting.zookeeper.ZookeeperClient connect(org.apache.dubbo.common.URL arg0) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg0;
String extName = url.getParameter("client", url.getParameter("transporter", "curator"));
if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter) name from url (" + url.toString() + ") use keys([client, transporter])");
org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter extension = (org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter)ExtensionLoader.getExtensionLoader(org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter.class).getExtension(extName);
return extension.connect(arg0);
}
}
3. 方法名稱有何用
spring 依賴注入可以根據方法名稱、然後在根據類型注入具體的實現。
org.apache.dubbo.config.spring.extension.SpringExtensionFactory#getExtension
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) {
if (context.containsBean(name)) {
Object bean = context.getBean(name);
if (type.isInstance(bean)) {
return (T) bean;
}
}
}
logger.warn("No spring extension (bean) named:" + name + ", try to find an extension (bean) of type " + type.getName());
if (Object.class == type) {
return null;
}
for (ApplicationContext context : CONTEXTS) {
try {
return context.getBean(type);
} catch (NoUniqueBeanDefinitionException multiBeanExe) {
logger.warn("Find more than 1 spring extensions (beans) of type " + type.getName() + ", will stop auto injection. Please make sure you have specified the concrete parameter type and there's only one extension of that type.");
} catch (NoSuchBeanDefinitionException noBeanExe) {
if (logger.isDebugEnabled()) {
logger.debug("Error when get spring extension(bean) for type:" + type.getName(), noBeanExe);
}
}
}
logger.warn("No spring extension (bean) named:" + name + ", type:" + type.getName() + " found, stop get bean.");
return null;
}
三、源碼流程
無論是Adaptive 或者直接調用,都會觸發 getExtension這個方法,獲取擴展的具體的實現。
DynamicConfigurationFactory factories = ExtensionLoader
.getExtensionLoader(DynamicConfigurationFactory.class)
.getExtension(url.getProtocol());
ZookeeperTransporter zookeeperTransporter = ExtensionLoader.getExtensionLoader(ZookeeperTransporter.class).getAdaptiveExtension();
ZookeeperTransporter$Adaptive 中根據參數調用的。
org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter extension = (org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter)ExtensionLoader.getExtensionLoader(org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter.class).getExtension(extName);
- org.apache.dubbo.common.extension.ExtensionLoader#getExtension
- org.apache.dubbo.common.extension.ExtensionLoader#createExtension
- org.apache.dubbo.common.extension.ExtensionLoader#injectExtension
- org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory#getExtension
- org.apache.dubbo.common.extension.factory.SpiExtensionFactory#getExtension