dubbo源码解析-SPI机制

news2024/9/27 23:22:56

SPI,Service Provider Interface,服务提供者接口,是一种服务发现机制。

JDK 的 SPI 规范

JDK 的 SPI 规范规定:

 接口名:可随意定义

 实现类名:可随意定义

 提供者配置文件路径:其查找的目录为 META-INF/services

 提供者配置文件名称:接口的全限定性类名,没有扩展名。

 提供者配置文件内容:该接口的所有实现类的全限类性类名写入到该文件中,一个类名占一行

代码示列:

package com.sqz.spi.jdk;

public interface Human {

    public void say();
}



package com.sqz.spi.jdk;

public class Huang implements Human{
    @Override
    public void say() {
        System.out.println("黄种人");
    }
}



package com.sqz.spi.jdk;

import java.util.Iterator;
import java.util.ServiceLoader;

public class Run {

    public static void main(String[] args) {
        ServiceLoader<Human> load = ServiceLoader.load(Human.class);
        Iterator<Human> iterator = load.iterator();
        while(iterator.hasNext()) {
            Human next = iterator.next();
            next.say();
        }
    }

}

配置文件在META_INFO下新建services目录,目录下新增文件名称为SPI接口全类名:

 文件内容为实现类全类名:

com.sqz.spi.jdk.Huang

Dubbo 的 SPI

Dubbo 并没有直接使用 JDK 的 SPI,而是在其基础之上对其进行了改进。

规范说明

Dubbo 的 SPI 规范是:

接口名:可以随意定义 实现类名:在接口名前添加一个用于表示自身功能的“标识前辍”字符串 提供者配置文件路径:在依次查找的目录为
META-INF/dubbo/internal
META-INF/dubbo
META-INF/services
提供者配置文件名称:接口的全限定性类名,无需扩展名
提供者配置文件内容:文件的内容为 key=value 形式,
value为该接口的实现类的全限类性类名,key 可以随意,但一般为该实现类的“标识前辍”(首字母小写)。一个类名占一行。
提供者加载:ExtensionLoader 类相当于 JDK SPI 中的 ServiceLoader
类,用于加载提供者配置文件中指定的实现类,并创建相应的实例。

 代码示列:

项目加入maven依赖:

 <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-common</artifactId>
            <version>3.0.2</version>
 </dependency>

测试代码:

package com.sqz.spi.dubbo;

import org.apache.dubbo.common.extension.SPI;

@SPI
public interface Order {
    String way();
}



package com.sqz.spi.dubbo;

public class AlipayOrder implements Order{
    @Override
    public String way() {
        return "支付宝支付";
    }
}


package com.sqz.spi.dubbo;

public class WeChatOrder implements Order{
    @Override
    public String way() {
        return "微信支付方式";
    }
}



package com.sqz.spi.dubbo;

import org.apache.dubbo.common.extension.ExtensionLoader;

public class Run {

    public static void main(String[] args) {
        ExtensionLoader<Order> extensionLoader = ExtensionLoader.getExtensionLoader(Order.class);
        Order alipay = extensionLoader.getExtension("alipay");
        System.out.println(alipay.way());
        Order wechat = extensionLoader.getExtension("wechat");
        System.out.println(wechat.way());
    }
}


配置文件可以在以下任意目录

META-INF/dubbo/internal
META-INF/dubbo
META-INF/services

这里在META_INFO下新建services目录,目录下新增文件名称为SPI接口全类名:

内容为键值对:

alipay=com.sqz.spi.dubbo.AlipayOrder
wechat=com.sqz.spi.dubbo.WeChatOrder

Adaptive 机制

Adaptive 机制,即扩展类的自适应机制。即其可以指定想要加载的扩展名,也可以不指定。若不指定,则直接加载默认的扩展类。即其会自动匹配,做到自适应。其是通过@Adaptive注解实现的。

@Adaptive 注解可以修饰类与方法,其作用不一样。

@Adaptive 修饰类

代码测试:

package com.sqz.adaptive.clazz;

import org.apache.dubbo.common.extension.SPI;

@SPI("alipay")
public interface Order {
    String way();
}


package com.sqz.adaptive.clazz;

public class WechatOrder implements Order {
    @Override
    public String way() {
        return "微信支付";
    }
}


package com.sqz.adaptive.clazz;

public class AlipayOrder implements Order {
    @Override
    public String way() {
        return "支付宝支付";
    }
}



package com.sqz.adaptive.clazz;

import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.utils.StringUtils;

@Adaptive
public class AdativeOrder implements Order {

    private String orderName;

    public void setOrderName(String orderName) {
        this.orderName = orderName;
    }

    public String way(){
        ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
        String name = orderName;
        Order order;
        if(StringUtils.isEmpty(name)) {
            order =  loader.getDefaultExtension();
        } else {
            order = loader.getExtension(name);
        }
        return order.way();
    }

}


package com.sqz.adaptive.clazz;

import org.apache.dubbo.common.extension.ExtensionLoader;

public class Run {

    public static void main(String[] args) {
       // extracted1();
        extracted2();
    }

    private static void extracted1() {
        ExtensionLoader<Order> extensionLoader = ExtensionLoader.getExtensionLoader(Order.class);
        Order order = extensionLoader.getAdaptiveExtension();
        ((AdativeOrder)order).setOrderName("alipay");
        System.out.println(order.way());
    }

    private static void extracted2() {
        ExtensionLoader<Order> extensionLoader = ExtensionLoader.getExtensionLoader(Order.class);
        Order order = extensionLoader.getAdaptiveExtension();
        System.out.println(order.way());
    }

}

配置文件:

 配置文件内容:

adative=com.sqz.adaptive.clazz.AdativeOrder
alipay=com.sqz.adaptive.clazz.AlipayOrder
weichat=com.sqz.adaptive.clazz.WechatOrder

Adaptive方法

 Adaptive 方法的定义规范仅一条:其参数包含 URL 类型的参数,或参数可以获取到 URL 类型的值。方法调用者是通过URL 传递要加载的扩展名的。

注意该URL类,是dubbo自己的:org.apache.dubbo.common.URL

看案例:

package com.sqz.adaptive.method;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;

@SPI
public interface Order {

    String way();

    @Adaptive
    String pay(URL url);
}


package com.sqz.adaptive.method;

import org.apache.dubbo.common.URL;

public class AlipayOrder implements  Order{
    @Override
    public String way() {
        System.out.println("--way-使用支付宝支付");
        return "支付宝支付";
    }

    @Override
    public String pay(URL url) {
        System.out.println("--pay-使用支付宝支付");
        return "pay 支付宝支付";
    }
}


package com.sqz.adaptive.method;

import org.apache.dubbo.common.URL;

public class WechatOrder implements Order{
    @Override
    public String way() {
        System.out.println("--wechat-使用微信支付");
        return "微信支付";
    }

    @Override
    public String pay(URL url) {
        System.out.println("--pay-使用微信支付");
        return "pay 微信支付";
    }
}



package com.sqz.adaptive.method;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;

public class Run {

    public static void main(String[] args) {
        extracted();
    }

    private static void extracted() {
        ExtensionLoader<Order> extensionLoader = ExtensionLoader.getExtensionLoader(Order.class);
        Order order = extensionLoader.getAdaptiveExtension();
        URL url = URL.valueOf("http://localhost:8080?order=alipay");
        System.out.println(order.pay(url));
       // System.out.println(order.way()); //报错UnsupportedOperationException,非@Adaptive方法不能自适应
    }
    
}

配置文件:

内容:

alipay=com.sqz.adaptive.method.AlipayOrder
wechat=com.sqz.adaptive.method.WechatOrder

需要注意的是:非@Adaptive方法不能自适应,报错UnsupportedOperationException

Wrapper 机制

Wrapper 机制,即扩展类的包装机制。就是对扩展类中的 SPI 接口方法进行增强,进行包装,是 AOP 思想的体现,是 Wrapper 设计模式的应用。一个 SPI 可以包含多个 Wrapper。

Wrapper 类规范

Wrapper 机制不是通过注解实现的,而是通过一套 Wrapper 规范实现的。
Wrapper 类在定义时需要遵循如下规范。(其实就是装饰设计模式的规范)

该类要实现 SPI 接口
该类中要有 SPI 接口的引用
该类中 SPI 接口实例是通过仅包含一个 SPI 接口参数的带参构造器传的(源码中判断是否是Wrapper类是以这个为标准的)
在接口实现方法中要调用 SPI 接口引用对象的相应方法
该类名称以 Wrapper 结尾

 代码实例

package com.sqz.wrapper;

import org.apache.dubbo.common.extension.SPI;

@SPI
public interface Order {
    String way();
}


package com.sqz.wrapper;

public class AlipayOrder implements Order {
    @Override
    public String way() {
        System.out.println("支付宝支付");
        return "支付宝支付成功";
    }
}


package com.sqz.wrapper;

public class WeChatOrder implements Order {
    @Override
    public String way() {
        System.out.println("微信支付");
        return "微信支付成功";
    }
}


package com.sqz.wrapper;

public class OrderWrapper1 implements Order{

    private Order order;

    public OrderWrapper1(Order order) {
        this.order = order;
    }

    public  String way() {
        System.out.println("w1 way before");
        String way = order.way();
        System.out.println("w1 way after");
        return way;
    }

}


package com.sqz.wrapper;

public class OrderWrapper2 implements Order{

    private Order order;

    public OrderWrapper2(Order order) {
        this.order = order;
    }

    public  String way() {
        System.out.println("w2 way before");
        String way = order.way();
        System.out.println("w2 way after");
        return way;
    }

}


package com.sqz.wrapper;

import org.apache.dubbo.common.extension.ExtensionLoader;

public class Run {

    public static void main(String[] args) {
        ExtensionLoader<Order> extensionLoader = ExtensionLoader.getExtensionLoader(Order.class);
        Order wrapper = extensionLoader.getExtension("alipay");
        System.out.println(wrapper.way());
    }
}

配置文件:

配置文件内容:

alipay=com.sqz.wrapper.AlipayOrder
wechat=com.sqz.wrapper.WeChatOrder
wrapper1=com.sqz.wrapper.OrderWrapper1
wrapper2=com.sqz.wrapper.OrderWrapper2

运行结果:

 w2 way before
w1 way before
支付宝支付
w1 way after
w2 way after
支付宝支付成功

Activate 机制

用于激活扩展类的。
Activate 机制,即扩展类的激活机制。通过指定的条件来激活当前的扩展类。其是通过@Activate 注解实现的。

@Activate 注解

在@Activate 注解中共有五个属性,其中 before、after 两个属性已经过时,剩余有效属性还有三个。它们的意义为:

group:为扩展类指定所属的组别,是当前扩展类的一个标识。String[]类型,表示一个扩展类可以属于多个组。
value:为当前扩展类指定的 key(必须是META-INF/dubbo/internal/com.abc.spi.Order中配置的前缀),是当前扩展类的一个标识。String[]类型,表示一个扩展类可以有多个指定的 key。
order:指定筛选条件相同的扩展类的加载顺序。序号越小,优先级越高。默认值为 0。

案例:

package com.sqz.activate;

import org.apache.dubbo.common.extension.SPI;

@SPI
public interface Order {
    String way();
}





package com.sqz.activate;


import org.apache.dubbo.common.extension.Activate;

@Activate(group = {"online"})
public class AlipayOrder implements Order {
    @Override
    public String way() {
        System.out.println("支付宝支付");
        return "支付宝支付成功";
    }
}


package com.sqz.activate;

import org.apache.dubbo.common.extension.Activate;

@Activate(group = {"online","offline"})
public class CardOrder implements Order{
    @Override
    public String way() {
        System.out.println("card支付");
        return "card支付成功";
    }
}



package com.sqz.activate;

import org.apache.dubbo.common.extension.Activate;
@Activate(group = {"offline"})
public class CashOrder implements Order{
    @Override
    public String way() {
        System.out.println("Cash支付");
        return "Cash支付成功";
    }
}




package com.sqz.activate;

import org.apache.dubbo.common.extension.Activate;
@Activate(group = {"offline"},order = -1)
public class CouponOrder  implements Order{
    @Override
    public String way() {
        System.out.println("Coupon支付");
        return "Coupon支付成功";
    }
}


package com.sqz.activate;

import org.apache.dubbo.common.extension.Activate;

@Activate(group = {"online"})
public class WeChatOrder implements Order {
    @Override
    public String way() {
        System.out.println("微信支付");
        return "微信支付成功";
    }
}


package com.sqz.activate;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;

import java.util.List;

public class Run {

    public static void main(String[] args) {
        ExtensionLoader<Order> extensionLoader = ExtensionLoader.getExtensionLoader(Order.class);
        List<Order> activateExtensions = extensionLoader.getActivateExtension(URL.valueOf(""), "", "offline");
        for (Order order : activateExtensions) {
            System.out.println(order.way());
        }
    }
}

配置文件:

 

配置文件内容:

alipay=com.sqz.activate.AlipayOrder
card=com.sqz.activate.CardOrder
cash=com.sqz.activate.CashOrder
coupon=com.sqz.activate.CouponOrder
wechat=com.sqz.activate.WeChatOrder

运行结果:

Coupon支付
Coupon支付成功
card支付
card支付成功
Cash支付
Cash支付成功

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/130871.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

剑指offer(简单)

目录 数组中重复的数字 替换空格 从尾到头打印链表 用两个栈实现队列 斐波那契数列 青蛙跳台阶问题 旋转数组的最小数字 二进制中的1的个数 打印从1到最大的n位数 删除链表的节点 调整数组顺序使奇数位于偶数前面 链表中倒数第k个节点 反转链表 合并两个排序的链…

一文读懂tensorflow: 基本概念和API

文章目录前言tensorflow发展历程基本概念张量神经网络、层模型超参数损失函数交叉熵函数激活函数梯度和自动微分优化器tensorflow 2.x 和 tensorflow 1.xtensorflow开发流程tensorflow API张量的定义和运算示例张量的初始化方式梯度计算模型的搭建示例&#xff1a;MINST手写数字…

Casting out Primes: Bignum Arithmetic for Zero-Knowledge Proofs学习笔记

1. 引言 Polygon zero团队 Daniel Lubarov 和 Polygon zkEVM团队 Jordi Baylina 2022年10月联合发表的论文 《Casting out Primes: Bignum Arithmetic for Zero-Knowledge Proofs》。 受“casting out nines” 技术——做对9取模运算并提供概率性结果&#xff0c;启发&#x…

RocketMQ 的存储模型

文章目录1.整体概览2.数据文件3.消费文件4.索引文件1.整体概览 Producer &#xff1a;消息发布的角色&#xff0c;Producer 通过 MQ 的负载均衡模块选择相应的 Broker 集群队列进行消息投递&#xff0c;投递的过程支持快速失败并且低延迟。 Consumer &#xff1a;消息消费的角…

如何应用人工智能和机器学习来预测消费者的行为

应用AI和机器学习来预测消费者行为 在这篇文章中&#xff0c;我们将学习和分析一般的消费者行为。我们还将了解人工智能是如何帮助发现有价值的见解的&#xff0c;从而使公司做出正确的决定&#xff0c;以实现提供更好的价值和创造更好的收入的愿景。 我们还将通过一个案例进…

6.2 参数传递

文章目录传值参数指针形参传引用参数使用引用避免拷贝使用引用形参返回额外信息const形参和实参指针或引用形参与const尽量使用常量引用数组形参使用标记指定数组长度使用标准库规范显式传递一个数组的长度数组形参和const数组引用形参传递多维数组main&#xff1a;处理命令行选…

142.环形链表II

给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数…

java开发社区活动预约系统

简介 本系统主要是社区活动预约系统网站&#xff0c;社区管理员可以发布活动&#xff0c;社区居民和游客均可进行活动预约&#xff0c;管理员后台审核预约是否通过&#xff0c;居民可以填写活动感受&#xff0c;管理员查看感受后可以进行反馈。居民最多取消三次预约&#xff0…

我用Python做了个动图生成器,把一千个MM生成了GIF设置桌面,只为每天愉悦心情

文章目录序言代码实战序言 现在的年轻人都开始每天保温杯里泡枸杞&#xff0c;这怎么能行呢&#xff1f; 想要每天过的好&#xff0c;美女必然少不了&#xff0c;每天看美女&#xff0c;只为了愉悦心情&#xff0c;心情好了&#xff0c;才长寿。 于是怀揣着愉悦心情的想法&am…

【leetcode】剑指offer1

&#x1f308;1.Pow(x,n) -100.0 < x < 100.0-2^31 < n < 2^31-1n 是一个整数-10^4 < x^n < 10^4思路分析&#xff1a; 暴力求解直接一个for循环n个x相乘解决&#xff0c;但是你拿那代码怎么好意思拿高薪&#xff1f; 所以而且那个的时间复杂度是O(n),效率并…

[阿里云] 10分钟带你玩转阿里云ECS和云盘 (大数据上云必备)

前言 由于准备做一些离线计算和实时计算的模拟, 发现某些教程内的阿里云还挺好用的, 在这里把相关的经验分享给大家. 简单的心路历程: 起先笔者搭建了一套本地集群. 但是后来发现, 因为没用网络IP的反穿, 本地的集群的网络访问非常不便. 其次, 集群的启停, 网络和磁盘管理都非…

缓存原理的学习

在如今这个微服务分布式的大环境下,集群分布式部署 几乎 已经是我们每个人都熟知的了。 缓存也一样&#xff0c;对我们来说 &#xff0c;如果只是一个单体应用 &#xff0c; 那只要 有本地缓存就足以了&#xff0c;但是倘若分布式部署了很多台机器上&#xff0c;那我们该如何缓…

软考-操作系统

【考点梳理】 【进程管理】 考点1、进程的状态&#xff08;★★&#xff09; 【考法分析】 本考点主要考查形式主要是根据图示判断相关状态位置或状态变迁条件。 【要点分析】 操作系统三态模型如下图所示&#xff1a;操作系统五态模型&#xff1a;【备考点拨】 掌握操作…

vue2.0和vue3.0创建项目

由于vue项目依赖于nodejs&#xff0c;所以需要先安装它。没有nodejs去官网下载。 npm install --global vue-cli 国内npm网站很慢&#xff0c;可以使用淘宝镜像 npm install --registryhttps://registry.npm.taobao.org vue2.0创建项目&#xff1a; 进入到自己需要创建项目…

SAP给微信推送消息

导语&#xff1a;最近领导下发指令&#xff0c;要求研究SAP与微信&#xff0c;企业微信&#xff0c;钉钉&#xff0c;邮件推送消息的平台&#xff0c;类似于采购订单审批之后&#xff0c;可以通过以上软件给用户发消息&#xff0c;我认领了微信的部分。 整个研究过程是很痛苦的…

华为静态NAT、动态NAT、PAT端口复用

一、网络环境及TOP 1.1 R1 相当于内网的一台PC&#xff0c; IP&#xff1a;192.168.1.10 网关为 192.168.1.254 [R1]ip route-static 0.0.0.0 0 192.168.1.254 # R1配置默认路由&#xff08;网关&#xff09; 1.2 R2为出口路由器&#xff0c;分别连接内网R1及外网R3 1&…

7. 整数反转

题目链接&#xff1a;力扣 解题思路&#xff1a; 题目要求中有一句话&#xff1a;假设环境不允许存储 64 位整数&#xff08;有符号或无符号&#xff09;。 也就是说不能使用long类型来判断是否int溢出&#xff0c;只能使用int类型进行运算 首先对于一个整数的翻转比较简单…

学openCV,不会数字图像系统可不行

来源&#xff1a;投稿 作者&#xff1a;小灰灰 编辑&#xff1a;学姐 前言 在光照的情况下&#xff0c;通过成像系统将大自然中的物体拍摄出来&#xff0c;成像系统手机自带有&#xff0c;这里面我们关心的是分辨率&#xff0c;成像系统显示的点数越多&#xff0c;阵列越大&am…

HCIA静态试验(12.30-31复习)

目标实现&#xff1a; 2、首先进行子网划分 基于192.168.1.0 24划分 ‘一共7个路由器需要7个网段还有7个主干网 192.168.1.0/24 ----用于骨干 192.168.1.32/27 ----R1环回 192.168.1.32/28 192.168.1.48/28 192.168.1.64/27 --- R2环回 192.168.1.64/28 192.168.1.80/28 …

Java之网络相关概念

寒假又开始更新java了&#xff0c;之后更新的是b站教程韩顺平老师的课&#xff0c;编译器我从idea换成eclipse&#xff08;因为蓝桥杯只有eclipse&#xff0c;要先熟悉&#xff09; 1.网络相关概念 网络通信 网络 ip地址 1.简单来说ip地址是每一台主机的标识 类似于我们现…