百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

Dubbo SPI机制解析

toyiye 2024-05-25 20:11 21 浏览 0 评论

SPI核心机制主要依赖于ExtensionLoader类,在3.0版本中获取扩展点的方式跟2版本是有区别的,已经废弃了原本的方式,而是采用以下方式

ExtensionLoader<Protocol> extensionLoader = 
ApplicationModel.defaultModel().getExtensionLoader(Protocol.class);
Protocol http = extensionLoader.getExtension("http");
http.sendRequest("http hello");

ExtensionLoader的核心功能点:

  • 扩展点的加载
  • 扩展点的依赖注入
  • 扩展点的自适应
  • 以及扩展点的激活

1.extensionLoader.getExtension()方法流程

public T getExtension(String name) {
    //调用重载方法,跟进去
    T extension = getExtension(name, true);
    if (extension == null) {
        throw new IllegalArgumentException("Not find extension: " + name);
    }
    return extension;
}
public T getExtension(String name, boolean wrap) {
    
    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;
}
private T createExtension(String name, boolean wrap) {
    //获取类对象
    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null || unacceptableExceptions.contains(name)) {
        throw findException(name);
    }
    try {
        //先从实例map中取一下看是否存在实例
        T instance = (T) extensionInstances.get(clazz);
        if (instance == null) {
            //创建实例并放入map中
            extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz));
            //再从实例map中获取
            instance = (T) extensionInstances.get(clazz);
            //前置增强
            instance = postProcessBeforeInitialization(instance, name);
            //判断是否需要setter注入
            injectExtension(instance);
            //后置增强
            instance = postProcessAfterInitialization(instance, name);
        }
        //是否存在包装
        if (wrap) {
            List<Class<?>> wrapperClassesList = new ArrayList<>();
            if (cachedWrapperClasses != null) {
                wrapperClassesList.addAll(cachedWrapperClasses);
                wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                Collections.reverse(wrapperClassesList);
            }

            if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                for (Class<?> wrapperClass : wrapperClassesList) {
                    Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                    boolean match = (wrapper == null) ||
                        ((ArrayUtils.isEmpty(wrapper.matches()) || ArrayUtils.contains(wrapper.matches(), name)) &&
                            !ArrayUtils.contains(wrapper.mismatches(), name));
                    if (match) {
                        //装饰增强
                        instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                        //执行后置通知
                        instance = postProcessAfterInitialization(instance, name);
                    }
                }
            }
        }

        // Warning: After an instance of Lifecycle is wrapped by cachedWrapperClasses, it may not still be Lifecycle instance, this application may not invoke the lifecycle.initialize hook.
        initExtension(instance);
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
            type + ") couldn't be instantiated: " + t.getMessage(), t);
    }
}

2.Dubbo 依赖注入

依赖注入,实现类依赖的属性自动注入,类似spring的依赖注入。如果字段不想不注入,可以在set方法上使用DisableInject注解。被依赖注入的属性类的方法必须有@Adaptive注解,即这个类是一个自适应SPI。

3.扩展点原理

案例代码如下

@SPI
public interface Animal {
    @Adaptive
    String eat(URL url);
}
@Adaptive
public class Cat implements Animal{
    @Override
    public String eat(URL url) {
        return "Cat eat";
    }
}
public class Dog implements Animal{
    @Override
    public String eat(URL url) {
        return "Dog eat";
    }
}
public class Pig implements Animal{
    @Override
    public String eat(URL url) {
        return "Pig eat";
    }
}
public class Demo {


    public static void main(String[] args) {
        ExtensionLoader<Animal> extensionLoader = ExtensionLoader.getExtensionLoader(Animal.class);
        Animal animal = extensionLoader.getAdaptiveExtension();

        String cat = animal.eat(URL.valueOf("http://localhost?aa=pig"));
        System.out.println(cat);
    }
}

输出结果为Cat eat

一个SPI下扩展点只能有一个,原理代码如下

private Map<String, Class<?>> loadExtensionClasses() {
    cacheDefaultExtensionName();

    Map<String, Class<?>> extensionClasses = new HashMap<>();
    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
    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 loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
    if (!type.isAssignableFrom(clazz)) {
        throw new IllegalStateException();
    }
    if (clazz.isAnnotationPresent(Adaptive.class)) {
        //缓存扩展点
        cacheAdaptiveClass(clazz);
    } else if (isWrapperClass(clazz)) {
        
    } else {
       
    }
}



private void cacheAdaptiveClass(Class<?> clazz) {
    //只能有一个扩展点
    if (cachedAdaptiveClass == null) {
        cachedAdaptiveClass = clazz;
    } else if (!cachedAdaptiveClass.equals(clazz)) {
        throw new IllegalStateException("More than 1 adaptive class found: "
                + cachedAdaptiveClass.getName()
                + ", " + clazz.getName());
    }
}

注释掉Cat的@Adaptive,默认的实现code如下:

private Class<?> createAdaptiveExtensionClass() {
    String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
    ClassLoader classLoader = findClassLoader();
    org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    return compiler.compile(code, classLoader);
}
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Animal\$Adaptive implements com.doukill.dubbo.adaptive.Animal {
    public java.lang.String eat(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("animal", "pig");
        if(extName == null) throw new IllegalStateException("Failed to get extension (com.doukill.dubbo.adaptive.Animal) name from url (" + url.toString() + ") use keys(\[animal])");
        
        com.doukill.dubbo.adaptive.Animal extension = (com.doukill.dubbo.adaptive.Animal)ExtensionLoader.getExtensionLoader(com.doukill.dubbo.adaptive.Animal.class).getExtension(extName);
        return extension.eat(arg0);
    }
}



```
类URL中代码:
public String getParameter(String key) {
    String value = parameters.get(key);
    return StringUtils.isEmpty(value) ? parameters.get(DEFAULT_KEY_PREFIX + key) : value;
}

public String getParameter(String key, String defaultValue) {
    String value = getParameter(key);
    return StringUtils.isEmpty(value) ? defaultValue : value;
}




ExtensionLoader<Animal> extensionLoader = ExtensionLoader.getExtensionLoader(Animal.class);
Animal animal = extensionLoader.getAdaptiveExtension();

String cat = animal.eat(URL.valueOf("http://localhost?animal=cat"));
System.out.println(cat);

String pig = animal.eat(URL.valueOf("http://localhost?animal=pig"));
System.out.println(pig);

String dog = animal.eat(URL.valueOf("http://localhost?animal=dog"));
System.out.println(dog);

//报错 没有默认值,除非在SPI注解上加一个默认值
String def = animal.eat(URL.valueOf("http://localhost"));
System.out.println(def);

```
作用其实比较简单,就是想动态地指定 URL 中的参数,来动态切换实现类去执行业务逻辑,把一堆根据参数获取实现类的重复代码,全部封装到了代理类中,以达到充分灵活扩展的效果。

@Adaptive 注解其实是生成了一个自适应的代理类,每个 SPI 接口都有且仅有一个自适应扩展点。

然后跟踪了加载 SPI 资源文件的 loadDirectory 方法的源码,发现 @Adaptive 注解不仅可以

写在 SPI 接口的方法上,还可以写在 SPI 接口实现类上,并且在使用自适应扩展的时候,若实

现类有 @Adaptive 注解,则优先使用该实现类作为自适应扩展点。


要实现SPI的setter注入,必须要有@Adaptive注解,源码如下:

public String generate() {
    // no need to generate adaptive class since there's no adaptive method found.
    //这里必须要有注解方法
    if (!hasAdaptiveMethod()) {
        throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
    }

    StringBuilder code = new StringBuilder();
    return code.toString();
}



public class Bb$Adaptive implements com.doukill.dubbo.demo.Bb {
    public java.lang.String eat(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("bb");
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (com.doukill.dubbo.demo.Bb) " +
                    "name from url (" + url.toString() + ") use keys([bb])");
        com.doukill.dubbo.demo.Bb extension = (com.doukill.dubbo.demo.Bb) 
                ExtensionLoader.getExtensionLoader(com.doukill.dubbo.demo.Bb.class).getExtension(extName);
        return extension.eat(arg0);
    }
}



相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码