Dubbo SPI Adaptive 源码简单分析

本文总结了Dubbo SPI和Adaptive的源码分析,包括Dubbo SPI的基本操作,如获取加载器、扩展实例,以及加载外部配置。Dubbo SPI的自适应扩展点分为两种情况:@Adaptive注解在实现类上和接口的方法上,分别有不同的处理策略。自适应扩展实现了动态调用扩展子类,是一种有效的适配器模式。
摘要由CSDN通过智能技术生成

在学习过程中,对Dubbo SPI 和 Adaptive的源码分析,自己的理解做一个总结记录。

内容建立在已经了解DubboSPI和Adaptive扩展点情况下对源码分析的关键点做总结。

DubboSPI是什么呢? 等同于Java的SPI和Spring的SPI,为了满足从外部环境或者三方Jar包中加载和实例化扩展定制的类,通过一些规则去读取文本文件里的类信息,然后加载到JVM中再实例化。

SPI的区别:

所以Java的SPI必须是文件名等同于接口名,Dubbo也是,为了方便通过文件名就知道它是哪个接口的配置方便解析,Spring是使用Spring.factories文件通过KV的方式定义映射关系,所以配置里一定要是类的全路径名。

DubboSPI的基本操作:

// 获取一个Protocol的扩展实现dubboProtocol
Protocol protocol = ApplicationModel.defaultModel().getDefaultModule().getExtensionLoader(Protocol.class).getExtension("dubbo");

1. getExtensionLoader(): 从缓存中去查找指定类型的加载器,找不到就创建再放到缓存中

extensionLoadersMap.putIfAbsent(type, new ExtensionLoader<T>(type, this, scopeModel))

2. getExtension("dubbo"):从当前加载器的缓存中去查找指定name的实例,如果找不到就去创建。

 public T getExtension(String name, boolean wrap) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        String cacheKey = name;
        if (!wrap) {
            cacheKey += "_origin";
        }
        final Holder<Object> holder = getOrCreateHolder(cacheKey);
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
  // 第一次缓存中不存在,直接到这里创建实例并且放入缓存中
                    instance = createExtension(name, wrap);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

2.1 创建的入口:

instance = createExtension(name, wrap);
 private T createExtension(String name, boolean wrap) {
        // 先加载外部配置的Class并且放到缓存中,再从缓存中获取name对应的Class
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null || unacceptableExceptions.contains(name)) {
            throw findException(name);
        }
        try {
            T instance = (T) extensionInstances.get(clazz);
            if (instance == null) {
                extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz));
                instance = (T) extensionInstances.get(clazz);
                instance = postProcessBeforeInitialization(instance, name);
                injectExtension(instance);
                instance = postProcessAfterInitialization(instance, name);
            }
           省略 .....................
}

 

2.2 加载外部配置缓存并获取指定name

Class<?> clazz = getExtensionClasses().get(name);
private Map<String, Class<?>> getExtensionClasses() {
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
// 第一次缓存是空的所以初始化加载
                    classes = loadExtensionClasses();
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }

 

2.3 创建的时候先去缓存中获取所有的外部配置,当然第一次先初始化缓存:

loadExtensionClasses()

初始化就是读取所有的外部配置信息并解析,根据不同的策略类对应不同的文件路径

for (LoadingStrategy strategy : strategies) {
            loadDirectory(extensionClasses, strategy, type.getName());

            // compatible with old ExtensionFactory
            if (this.type == ExtensionInjector.class) {
                loadDirectory(extensionClasses, strategy, ExtensionFactory.class.getName());
            }
        }

以上就完成了根据一个接口动态的加载外部配置对应的扩展类,主要步骤

1)获取或创建一个加载器 getExtensionLoader()

2)根据name从缓存中获取或创建一个扩展类实例 getExtensionClasses().get(name)

2-1) 缓存不存在则需要读取一次外部配置加载解析。

比如Java中的Driver接口,不同的数据库厂商提供各自的驱动都在三方Jar包中,通过SPI获取Driver接口的外部类。

3.Dubbo SPI的自适应扩展点

// 通过自适应扩展点生成一个Protocol的适配器类
Protocol protocol = ApplicationModel.defaultModel().getDefaultModule().getExtensionLoader(Protocol.class).getAdaptiveExtension();

 @Adaptive可以注解在实现类上和接口的方法上,这两种用法有很大的区别。

3.1 @Adaptive注解在实现类上会直接从缓存中查找,优先级是最高的。找不到则尝试给当前接口生成一个适配器的类。

private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
// 如果实现类注解了@Adaptive直接返回
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
// 否则生成适配器类
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

3.2 生成适配器类是通过字节码的方式,先拼接一个Java类的字符串代码

String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();

拼接核心是针对加了@Adaptive注解的方法,对该方法生成一个模板内容,该代码内容主要是根据extName调用DubboSPI的方法,那么就完成了根据name动态调用扩展子类的效果,就是一种适配器方法。

// 模板方法内容,每个注解@Adaptive的方法生成这个模板
public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {

        if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        org.apache.dubbo.common.URL url = arg0.getUrl();
        // 通过不同的判断获取extName需要与SPI配置文件中KEY一致,处理规则在这里generateExtNameAssignment
        String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
        if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
        ScopeModel scopeModel = ScopeModelUtil.getOrDefault(url.getScopeModel(), org.apache.dubbo.rpc.Protocol.class);
    // 通过Dubbo的SPI根据extName获取扩展实现类再调用一样的方法,完成了适配器的效果
        org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)scopeModel.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.export(arg0);
    }

 自适应扩展点按@Adaptive注解在实现类上,优先级最高,如果存在就直接返回。

@Adaptive注解在接口的方法上,会通过字节码的方式动态生成一个适配器类,给注解的方法加上模板内容,以上就是模板内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值