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>'\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 类不属于扩展类范畴。