Dubbo-Dubbo SPI 依賴注入

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 依賴注入

image.png

2.2 獲取SPI的擴展實現類

image.png

2.3 返回擴展 ZookeeperTransporter$Adaptive

image.png

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
    在這裏插入圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章