dubbo源码解析-SPI机制

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支付成功

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值