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
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Dubbo-Admin 是 Dubbo 提供的一个可视化管理平台,用于监控和管理 Dubbo 服务。安装和部署 Dubbo-Admin 需要以下步骤: 1. 下载 Dubbo-Admin 的安装包,可以从 Dubbo 的官方 GitHub 仓库中获取。你可以访问 https://github.com/apache/dubbo-admin/releases 下载最新版本的安装包。 2. 解压下载的安装包到你选择的目录。你可以使用以下命令解压: ```shell unzip dubbo-admin-x.x.x.zip ``` 3. 进入解压后的目录,找到 `dubbo-admin-server` 目录。 4. 打开 `dubbo-admin-server` 目录下的 `src/main/resources/application.properties` 文件,修改其中的配置信息。 - 配置 Dubbo 注册中心的地址:`dubbo.registry.address=zookeeper://localhost:2181`,根据你实际的注册中心地址进行修改。 - 配置 Dubbo Admin 的访问端口:`server.port=8080`,根据你的需求进行修改。 5. 保存并关闭 `application.properties` 文件。 6. 在 `dubbo-admin-server` 目录下执行以下命令来构建 Dubbo-Admin: ```shell mvn clean package ``` 7. 构建完成后,进入 `target` 目录,执行以下命令来启动 Dubbo-Admin: ```shell java -jar dubbo-admin-x.x.x.jar ``` 8. 等待启动完成,访问 http://localhost:8080 即可进入 Dubbo-Admin 的管理界面。 请注意,安装和部署 Dubbo-Admin 需要满足以下依赖条件: - JDK 1.8 或以上版本 - Maven 3.x - ZooKeeper 3.x 或以上版本 希望以上步骤对你有帮助!如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值