其他分享
首页 > 其他分享> > Dubbo(八)内核解析(DubboSPI机制、Adaptive机制、Wrapper机制、Activate机制)

Dubbo(八)内核解析(DubboSPI机制、Adaptive机制、Wrapper机制、Activate机制)

作者:互联网

Dubbo 的内核解析

所谓 Dubbo 的内核是指,Dubbo 中所有功能都是基于它之上完成的,都是由它作为基础的。Dubbo 内核的工作原理由四部分构成:服务发现机制 SPI、自适应机制 Adaptive、包装机制 Wrapper 与激活机制 Activate。

Dubbo 通过这四种机制实现了对插件的 IoC、AOP,实现了对自动生成类的动态编译 Compile。

1. JDK 的 SPI 机制

1.1 简介

在这里插入图片描述

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

1.2 JDK 的 SPI 规范

JDK 的 SPI 规范规定:

接口名:可随意定义
实现类名:可随意定义 提供者配置文件路径:其查找的目录为 META-INF/services
提供者配置文件名称:接口的全限定性类名,没有扩展名。
提供者配置文件内容:该接口的所有实现类的全限类性类名写入到该文件中,一个类名占一行

1.3 代码举例 12-jdkspi

(1) 定义工程

创建一个 Maven 的 Java 工程。

(2) 定义接口

public interface SomeService {
    void hello();
}

(3) 定义两个实现类

public class OneServiceImpl implements SomeService {
    public void hello() {
        System.out.println("执行OneServiceImpl的hello()");
    }
}
public class TwoServiceImpl implements SomeService {
    public void hello() {
        System.out.println("执行TwoServiceImpl的hello()");
    }
}

(4) 创建目录与配置文件

在这里插入图片描述

(5) 定义配置文件内容

com.abc.service.OneServiceImpl
com.abc.service.TwoServiceImpl

(6) 定义服务消费者类

public class SPITest {
    public static void main(String[] args) {
        ServiceLoader<SomeService> loader = ServiceLoader.load(SomeService.class);
        Iterator<SomeService> iterator = loader.iterator();

        while (iterator.hasNext()) {
            SomeService service = iterator.next();
            service.hello();

            // OneServiceImpl service1 = new OneServiceImpl();
            // service1.hello();
            // 缺点:
            // 不能指定只调用其中某一个实现类
        }
    }
}

(7) 测试

在这里插入图片描述

(8) 源码解析

/**
 * A simple service-provider loading facility.
 * 一个简单的服务提供者加载工具。
 * 
 * ...
 * <p><a name="format"> A service provider is identified by placing a
 * <i>provider-configuration file</i> in the resource directory
 * <tt>META-INF/services</tt>.</a>  The file's name is the fully-qualified <a
 * href="../lang/ClassLoader.html#name">binary name</a> of the service's type.
 * The file contains a list of fully-qualified binary names of concrete
 * provider classes, one per line.  Space and tab characters surrounding each
 * name, as well as blank lines, are ignored.  The comment character is
 * <tt>'#'</tt> (<tt>'&#92;u0023'</tt>,
 * <font style="font-size:smaller;">NUMBER SIGN</font>); on
 * each line all characters following the first comment character are ignored.
 * The file must be encoded in UTF-8.
 * 
 * 通过在资源目录META-INF/services中放置提供者-配置文件,可以识别服务提供者。
 * 文件的名称是服务类型的完全限定二进制名称。该文件包含具体提供程序类的全限定二
 * 进制名称列表,每行一个。忽略每个名称周围的空格和制表符以及空白行。注释字符为
 * '#';在每行中,第一个注释字符之后的所有字符都将被忽略。该文件必须用UTF-8编码。
 * ...
 */
 public final class ServiceLoader<S> implements Iterable<S>{...}

2. Dubbo 的 SPI

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

2.1 规范说明

Dubbo 的 SPI 规范是:

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

Dubbo源码举例在这里插入图片描述配置文件:在这里插入图片描述文件内容:在这里插入图片描述

ExtensionLoader类getExtension(String)方法:
该方法可以根据“标识前缀”获取对应的实现类的实例,但它不是静态方法,所以使用前需要先获取ExtensionLoader实例。

//org.apache.dubbo.common.extension.ExtensionLoader#getExtension
/**
 * Find the extension with the given name. If the specified name is not found, then {@link IllegalStateException}
 * will be thrown.
 */
@SuppressWarnings("unchecked")
public T getExtension(String name) {
	//这个name就是“标识前辍”
    if (name == null || name.length() == 0) {
        throw new IllegalArgumentException("Extension name == null");
    }
    if ("true".equals(name)) {
        return getDefaultExtension();
    }
    Holder<Object> holder = cachedInstances.get(name);
    if (holder == null) {
        cachedInstances.putIfAbsent(name, new Holder<Object>());
        holder = cachedInstances.get(name);
    }
    Object instance = holder.get();
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                instance = createExtension(name);
                holder.set(instance);
            }
        }
    }
    return (T) instance;

静态方法getExtensionLoader可以获取ExtensionLoader实例:
该方法入参是一个Class,可以传需要扩展的接口的Class,即一个接口一个ExtensionLoader实例,这样不同接口的实现类如果前缀相同不会相互影响。

//org.apache.dubbo.common.extension.ExtensionLoader#getExtensionLoader
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
	//type就是接口
    if (type == null) {
        throw new IllegalArgumentException("Extension type == null");
    }
    if (!type.isInterface()) {
        throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
    }
    //传入的type,必须要有SPI注解标记为SPI接口
    if (!withExtensionAnnotation(type)) {
        throw new IllegalArgumentException("Extension type(" + type +
                ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
    }

    ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    if (loader == null) {
        EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
        loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    }
    return loader;
}

DubboSPI有一个注解@SPI,用来标识接口是SPI接口,这样SPI接口就能和普通接口进行区分。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SPI {

    /**
     * default extension name
     */
    String value() default "";

}

举例在这里插入图片描述在这里插入图片描述在这里插入图片描述

2.2 Dubbo 的 SPI 举例 13-dubbospi

下面将实现一个下单功能。其支付方式仅支持支付宝或微信两种方式。即这里要定义一个 SPI 接口,其存在两个扩展类。

(1) 创建工程

创建一个 Maven 的 Java 工程。

(2) 导入依赖

<properties>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	<maven.compiler.source>1.8</maven.compiler.source>
	<maven.compiler.target>1.8</maven.compiler.target>
</properties>

<dependencies>
	<!--Dubbo依赖-->
	<dependency>
		<groupId>org.apache.dubbo</groupId>
		<artifactId>dubbo</artifactId>
		<version>2.7.3</version>
	</dependency>
	
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.11</version>
		<scope>test</scope>
	</dependency>
</dependencies>

(3) 定义 SPI 接口

/**
 * 下单接口
 */
@SPI("wechat")
public interface Order {
    // 支付方式
    String way();
}

(4) 定义两个扩展类

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

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

(5) 定义扩展类配置文件

META-INF/dubbo/internal/com.abc.spi.Order

alipay=com.abc.spi.extension.AlipayOrder
wechat,wechat2=com.abc.spi.extension.WeChatOrder

(6) 定义测试类

public class OrderTest {

    @Test
    public void test01() {
        ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);

        Order alipay = loader.getExtension("alipay");
        System.out.println(alipay.way());
        //同一个实现类,多个名称,是同一个实例
        Order wechat = loader.getExtension("wechat");
        System.out.println(wechat.way());
        Order wechat2 = loader.getExtension("wechat2");
        System.out.println(wechat2 == wechat);
    }

    @Test
    public void test02() {
        ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
        //验证不指定前缀的情况,这里会报错
        //需要依靠 Adaptive 机制
        Order alipay = loader.getExtension(null);
        System.out.println(alipay.way());
    }

}

test01:在这里插入图片描述
test02:在这里插入图片描述

3. Adaptive 机制

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

3.1 @Adaptive 注解

@Adaptive 注解可以修饰类与方法,其作用相差很大。

(1) @Adaptive 修饰类

被@Adapative 修饰的 SPI 接口扩展类称为 Adaptive 类(自适应类),表示该 SPI 扩展类会按照该类中指定的方式获取,即用于固定实现方式。其是装饰者设计模式的应用。

在dubbo框架中,一共就2个Adaptive类
AdaptiveCompiler:在这里插入图片描述AdaptiveExtensionFactory:在这里插入图片描述

(2) @Adaptive 修饰方法

被@Adapative 修饰 SPI 接口中的方法称为 Adaptive 方法。在 SPI 扩展类中若没有找到Adaptive 类,但系统却发现了 Adapative 方法,就会根据 Adaptive 方法自动为该 SPI 接口动态生成一个 Adaptive 扩展类,并自动将其编译。例如 Protocol 接口中就包含两个 Adaptive方法。

dubbo框架中Adaptive方法很多:
Protocol在这里插入图片描述Cluster:在这里插入图片描述

3.2 Adaptive 类代码举例 14-adaptiveclass

(1) 创建工程

复制 13-dubbospi 工程,在其基础之上修改。

(2) 定义 adaptive 扩展类

@Adaptive
public class AdaptiveOrder implements Order {
	//用于指定要加载的扩展名称
    private String orderName;

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

    @Override
    public String way() {
        ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
        Order order;
        if(StringUtils.isEmpty(orderName)) {
	        //若没有指定请求参数,则获取默认的扩展类实例
            order = loader.getDefaultExtension();
        } else {
	        //若指定了请求参数,则获取指定的扩展类实例
            order = loader.getExtension(orderName);
        }
        return order.way();
    }
}

(3) 修改扩展类配置文件

META-INF/dubbo/internal/com.abc.spi.Order

alipay=com.abc.spi.extension.AlipayOrder
wechat=com.abc.spi.extension.WeChatOrder
adaptive=com.abc.spi.extension.AdaptiveOrder

(4) 测试 1

public class OrderTest {
    @Test
    public void test01() {
	    //获取Order扩展类的加载对象Loader
        ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
        //获取Order接口的自适应扩展类实例
        Order adaptiveExtension = loader.getAdaptiveExtension();
        //指定要加载的扩展名
        ((AdaptiveOrder)adaptiveExtension).setOrderName("wechat");
        System.out.println(adaptiveExtension.way());
    }
}

在这里插入图片描述

(5) 测试 2

public class OrderTest {
    @Test
    public void test02() {
	    //获取Order扩展类的加载对象Loader
        ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
        //获取Order接口的自适应扩展类实例
        Order adaptiveExtension = loader.getAdaptiveExtension();
        // ((AdaptiveOrder)adaptiveExtension).setOrderName("wechat");
        //没有指定会加载默认扩展类
        System.out.println(adaptiveExtension.way());
    }
}

在这里插入图片描述

(5) 测试 3

public class OrderTest {
    @Test
    public void test03() {
        ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
        // 获取真正的扩展类
        // adaptive类不属于SPI接口的真正扩展类
        Set<String> extensions = loader.getSupportedExtensions();
        System.out.println(extensions);
    }
}

在这里插入图片描述

3.3 Adaptive 方法规范

下面我们准备要定义 Adaptive 方法。那么 Adaptive 方法的定义有什么要求呢?我们通过查看动态生成的 Adaptive 类来总结 Adaptive 方法的要求。

(1) 动态生成 Adaptive 类格式

其中
adaptiveMethod表示SPI接口中被@Adaptive标记的方法
unAdaptiveMethod表示没有被标记的方法

package <SPI 接口所在包>;
public class SPI 接口名$Adpative implements SPI 接口 {
	public adaptiveMethod (arg0, arg1, ...) {
		// 注意,下面的判断仅对 URL 类型,或可以获取到 URL 类型值的参数进行判断
		// 例如,dubbo 的 Invoker 类型中就包含有 URL 属性
		if(arg1==null) throw new IllegalArgumentException(异常信息);
		if(arg1.getUrl()==null) throw new IllegalArgumentException(异常信息);
		
		URL url = arg1.getUrl();
		// 其会根据@Adaptive 注解上声明的 Key 的顺序,从 URL 获取 Value,
		// 作为实际扩展类。若有默认扩展类,则获取默认扩展类名;否则获取
		// 指定扩展名名。
		String extName = url.get 接口名() == null?默认扩展前辍名:url.get 接口名();
		
		if(extName==null) throw new IllegalStateException(异常信息);
		
		SPI 接口 extension = ExtensionLoader.getExtensionLoader(SPI 接口.class)
		.getExtension(extName);
		
		return extension. adaptiveMethod(arg0, arg1, ...);
	}
	//未被标记的方法尝试用自适应类调用会抛异常
	public unAdaptiveMethod( arg0, arg1, ...) {
		throw new UnsupportedOperationException(异常信息);
	}
}

(2) 方法规范

从前面的动态生成的 Adaptive 类中的 adaptiveMethod()方法体可知,其对于要加载的扩展名的指定方式是通过 URL 类型的方法参数指定的。所以对于 Adaptive 方法的定义规范仅一条:其参数包含 URL 类型的参数,或参数可以获取到 URL 类型的值。方法调用者是通过URL 传递要加载的扩展名的。

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

3.4 Adaptive 方法代码举例 14-adaptivemethod

(1) 创建工程

复制 14-adaptiveclass 工程,在其基础之上修改。

(2) 修改 SPI 接口

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

// 下单接口
@SPI("alipay")
public interface Order {
    String way();

    @Adaptive
    String pay(URL url);
}

(3) 修改两个扩展类

// 支付宝支付下单
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 "使用支付宝支付";
    }
}

// 微信支付下单
public class WeChatOrder implements Order {
    @Override
    public String way() {
        System.out.println("--- 微信way() ---");
        return "微信支付方式";
    }

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

(4) 删除原来的 Adaptive 类

若存在 Adaptive 类,即使定义了 Adaptive 方法,其执行的也是 Adaptive 类,所以这里要首先将 Adaptive 类删除。

(5) 定义扩展类配置文件

由于 Adaptive 类已经删除,所以在配置文件中也需要将 Adaptive 类的注册也删除。
META-INF/dubbo/internal/com.abc.spi.Order

alipay=com.abc.spi.extension.AlipayOrder
wechat=com.abc.spi.extension.WeChatOrder

(6) 测试 1

public class OrderTest {
    @Test
    public void test01() {
        ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);

        Order adaptiveExtension = loader.getAdaptiveExtension();
        // 模拟一个URL
        URL url = URL.valueOf("xxx://localhost/ooo");
        System.out.println(adaptiveExtension.pay(url));
    }
}

在这里插入图片描述
在这里插入图片描述

(7) 测试 2

public class OrderTest {
    @Test
    public void test02() {
        ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);

        Order adaptiveExtension = loader.getAdaptiveExtension();
        // 模拟一个URL
        // SPI接口为GoodsOrder  此时的url为xxx://localhost/ooo?goods.order=wechat
        URL url = URL.valueOf("xxx://localhost/ooo?order=wechat");
        System.out.println(adaptiveExtension.pay(url));
        System.out.println(adaptiveExtension.way());
    }
}

在这里插入图片描述

3.5 源码简单分析

简单看下org.apache.dubbo.common.extension.ExtensionLoader#getAdaptiveExtension方法实现(后期会详细说):

public T getAdaptiveExtension() {
    Object instance = cachedAdaptiveInstance.get();
    if (instance == null) {
        if (createAdaptiveInstanceError == null) {
            synchronized (cachedAdaptiveInstance) {
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
	                    //跟这个
                        instance = createAdaptiveExtension();
                        cachedAdaptiveInstance.set(instance);
                    } catch (Throwable t) {
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
                    }
                }
            }
        } else {
            throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
        }
    }

    return (T) instance;
}

//org.apache.dubbo.common.extension.ExtensionLoader#createAdaptiveExtension
private T createAdaptiveExtension() {
    try {
	    //主要先看下getAdaptiveExtensionClass
	    //获取自适应扩展类
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
    }
}

private Class<?> getAdaptiveExtensionClass() {
    getExtensionClasses();
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    //如果是Adaptive方法的话,这里肯定没有CLass
    //所以跟createAdaptiveExtensionClass
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

private Class<?> createAdaptiveExtensionClass() {
	//这个code就是动态生成的Adaptive类
    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);
}

通过断点,可以截取到code内容在这里插入图片描述

package com.abc.spi;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Order$Adaptive implements com.abc.spi.Order {
    public java.lang.String pay(org.apache.dubbo.common.URL arg0)  {
        if (arg0 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg0;
        //这里默认是接口名,首字母小写,可以通过@Adaptive指定key
        String extName = url.getParameter("order", "alipay");
        if(extName == null) throw new IllegalStateException("Failed to get extension (com.abc.spi.Order) name from url (" + url.toString() + ") use keys([order])");
        com.abc.spi.Order extension = (com.abc.spi.Order)ExtensionLoader.getExtensionLoader(com.abc.spi.Order.class).getExtension(extName);
        return extension.pay(arg0);
    }
    public java.lang.String way()  {
        throw new UnsupportedOperationException("The method public abstract java.lang.String com.abc.spi.Order.way() of interface com.abc.spi.Order is not adaptive method!");
    }
}

在这里插入图片描述

4. Wrapper 机制

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

4.1 Wrapper 类规范

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

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

dubbo源码中例子:在这里插入图片描述以ProtocolFilterWrapper为例:在这里插入图片描述在这里插入图片描述在这里插入图片描述

4.2 代码举例 15-wrapper

复制 14-adaptivemethod 工程,在其基础之上修改。

(1) 定义两个 wrapper 类

public class OrderWrapper implements Order {

    private Order order;

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

    @Override
    public String way() {
        System.out.println("before-这是wrapper对way()的增强");
        String way = order.way();
        System.out.println("after-这是wrapper对way()的增强");
        return way;
    }

    @Override
    public String pay(URL url) {
        System.out.println("before-这是wrapper对pay()的增强");
        String pay = order.pay(url);
        System.out.println("after-这是wrapper对pay()的增强");
        return pay;
    }
}


public class OrderWrapper2 implements Order {

    private Order order;

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

    @Override
    public String way() {
        System.out.println("before222-这是wrapper对way()的增强");
        String way = order.way();
        System.out.println("after222-这是wrapper对way()的增强");
        return way;
    }

    @Override
    public String pay(URL url) {
        System.out.println("before222-这是wrapper对pay()的增强");
        String pay = order.pay(url);
        System.out.println("after222-这是wrapper对pay()的增强");
        return pay;
    }
}

(2) 修改扩展类配置文件

将这两个 wrapper 注册到扩展类配置文件中。
META-INF/dubbo/internal/com.abc.spi.Order

alipay=com.abc.spi.extension.AlipayOrder
wechat=com.abc.spi.extension.WeChatOrder
wrapper=com.abc.spi.extension.OrderWrapper
wrapper2=com.abc.spi.extension.OrderWrapper2

(3) 测试

public class OrderTest {

    @Test
    public void test01() {
        ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);

        Order adaptiveExtension = loader.getAdaptiveExtension();
        URL url = URL.valueOf("xxx://localhost/ooo");
        System.out.println(adaptiveExtension.pay(url));
    }

    @Test
    public void test02() {
        ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
        Set<String> extensions = loader.getSupportedExtensions();
        System.out.println(extensions);
    }

}

test01:
在这里插入图片描述
test02:在这里插入图片描述
说明wrapper 类不属于扩展类

5. Activate 机制

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

5.1 @Activate 注解

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

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

5.2 代码举例

(1) 创建工程
复制 13-dubbospi 工程,在其基础之上修改。

(2) 修改扩展类 AlipayOrder
支付宝属于线上支付,group为online

// @Activate的作用就是为当前扩展类添加一个用于筛选的标签,
// group与value的关系为  与
@Activate(group = "online", value = "alipay")
public class AlipayOrder implements Order {
    @Override
    public String way() {
        System.out.println("--- 支付宝way() ---");
        return "支付宝支付方式";
    }
}

(3) 修改扩展类 WeChatOrder

微信属于线上支付,group为online

@Activate(group = "online", value = "wechat")
public class WeChatOrder implements Order {
    @Override
    public String way() {
        System.out.println("--- 微信way() ---");
        return "微信支付方式";
    }
}

(4) 定义扩展类 CardOrder

银行卡线上、线下都行,group为online、offline

@Activate(group = {"online", "offline"}, order = 3)
public class CardOrder implements Order {
    @Override
    public String way() {
        System.out.println("--- 银行卡way() ---");
        return "银行卡支付方式";
    }
}

(5) 定义扩展类 CashOrder

@Activate(group = "offline", order = 4)
public class CashOrder implements Order {
    @Override
    public String way() {
        System.out.println("--- 现金way() ---");
        return "现金支付方式";
    }
}

(6) 定义扩展类 CouponOrder

@Activate(group = "offline", order = 5)
public class CouponOrder implements Order {
    @Override
    public String way() {
        System.out.println("--- 购物券way() ---");
        return "购物券支付方式";
    }
}

(7) 注册扩展类

META-INF/dubbo/internal/com.abc.spi.Order

alipay=com.abc.spi.extension.AlipayOrder
wechat=com.abc.spi.extension.WeChatOrder
card=com.abc.spi.extension.CardOrder
cash=com.abc.spi.extension.CashOrder
coupon=com.abc.spi.extension.CouponOrder

(8) 测试类

test01:

@Test
public void test01() {
    ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
    URL url = URL.valueOf("xxx://localhost/ooo");
    // 激活group为online的所有扩展类
    List<Order> activateExtensions = loader.getActivateExtension(url, "", "online");
    for (Order order : activateExtensions) {
        System.out.println(order.way());
    }
}

在这里插入图片描述
微信和支付宝order都是0,后注册的优先级更高

test02:

@Test
public void test02() {
    ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
    URL url = URL.valueOf("xxx://localhost/ooo");
    // 激活group为offline的所有扩展类
    List<Order> activateExtensions = loader.getActivateExtension(url, "", "offline");
    for (Order order : activateExtensions) {
        System.out.println(order.way());
    }
}

在这里插入图片描述
test03:

@Test
public void test03() {
    ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
    URL url = URL.valueOf("xxx://localhost/ooo?xxx=alipay");
    // 激活group为online、或  功能性扩展名为alipay扩展类
    // "xxx"代表可以随意
    List<Order> activateExtensions = loader.getActivateExtension(url, "xxx", "online");
    for (Order order : activateExtensions) {
        System.out.println(order.way());
    }
}

在这里插入图片描述

@Activate(group = "online", value = "alipay")
public class AlipayOrder implements Order {...}

@Activate(group = "online", value = "wechat")
public class WeChatOrder implements Order {...}

@Activate(group = {"online", "offline"}, order = 3)
public class CardOrder implements Order {...}

由此可见group和value是与的关系,但是因为CardOrder没有指定value,所以也会被匹配上

test04:

@Test
public void test04() {
    ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
    Set<String> extensions = loader.getSupportedExtensions();
    System.out.println(extensions);
}

在这里插入图片描述

6. 总结

在扩展类配置文件中可能会存在四种类:普通扩展类、Adaptive 类、Wrapper 类,及Activate 类。

它们的共同点是,都实现了 SPI 接口。
Adaptive 类与 Activate 类都是通过注解定义的,而 Wrapper 则是通过一套规范定义的。
一个 SPI 接口的 Adaptive 类只能有一个(无论是否是自动生成的),而 Wrapper 类与Activate 类可以有多个。
只有普通扩展类与 Activate 类属于扩展类范畴,Adaptive 类与 Wrapper 类不属于扩展类范畴。

标签:Dubbo,Activate,ExtensionLoader,class,Order,Adaptive,way,机制,public
来源: https://blog.csdn.net/chenandyuan/article/details/119043763