dubboSPI扩展机制基本使用,自动包装

介绍

  DubboSPI是基于JavaSPI的一个扩展与补充。
https://dubbo.apache.org/zh/docs/v2.7/dev/spi/

优势:

  1. Java SPI 每次都会把所有实现类都加载并实例化(是在迭代器迭代的时候创建实例),而 Dubbo SPI 是分两段创建实例,先进行类加载,然后在使用到具体实现的时候才实例化,并且 Dubbo SPI 大量使用缓存,会把 Class 对象和实例对象都缓存起来,性能更好。
  2. Java SPI 在类加载失败的时候难以定位异常;
  3. Dubbo SPI 还支持 IOC 和 AOP 。

用法:

  1. 基本使用
  2. 扩展点自动包装
  3. 扩展点自适应
  4. 扩展点自动适配
  5. 扩展点自动激活

基本使用

  dubboSPI的基本使用和javaSPI并无太大差异。

主要区别:

JDK标准的SPI会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。dubboSPI可以选择性实例某个实现。

  1. dubboSPI需要SPI注解。

  2. 文件夹命名格式不同,文件内容格式不同。

    dubbo为META-INF/dubbo,文件内容为key=value(实现类全限定名)。
    java为META-INF/services,文件内容直接为实现类全限定名。

  3. 启动的API不同。

    dubbo为ExtensionLoader。
    java为ServiceLoader,是java。

  4. java会扫描所有的类,无法按需加载,dubbo可以。

定义一个接口

  需要使用SPI注解。

1
2
3
4
5
// dubbo需要SPI注解,SPI注解的值为默认的实现
@SPI
public interface ExtendService {
void sayHello();
}

定义实现类

1
2
3
4
5
6
7
8
public class ExtendServiceImpl implements ExtendService {

@Override
public void sayHello(){
System.out.println("hello");
}

}

创建文件夹

  在classpath下创建指定文件夹及文件

1
2
3
4
5
6
7
8
9
10
11
12
# 文件夹
# 与javaSPI文件夹不同
MATA-INF/dubbo

# 文件名(接口的全路径名)
com.lin.extendwrapper.extend.ExtendService

# 文件内容
# key=value形式,与javaSPI不同格式
# 可以有多个实现
# java内容为实现类全限定名
ExtendService=com.lin.extendwrapper.extend.impl.ExtendServiceImpl

程序入口

1
2
3
4
5
6
7
8
9
public class ExtendWrapper {

public static void main(String[] args) {
// 与javaSPI的API不同
ExtensionLoader<ExtendService> extensionLoader = ExtensionLoader.getExtensionLoader(ExtendService.class);
ExtendService extendService = extensionLoader.getExtension("ExtendService");
extendService.sayHello();
}
}

扩展点自动包装

  ExtensionLoader在加载扩展点时,如果加载到的扩展点有拷贝构造函数,则判定为扩展点Wrapper类。
  这个类似于AOP。

增加一个实现类(包装类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ExtendServiceImplWrapper implements ExtendService {

private ExtendService extendService;

public ExtendServiceImplWrapper(ExtendService extendService) {
this.extendService = extendService;
}

@Override
public void sayHello() {
System.out.println("before");
extendService.sayHello();
System.out.println("after");
}
}

修改META-INF下文件内容

1
2
3
ExtendService=com.lin.extendwrapper.extend.impl.ExtendServiceImpl
# 增加一行
ExtendService=com.lin.extendwrapper.extend.impl.ExtendServiceImplWrapper

总结

  扩展点自动包装,会在通过ExtensionLoader获取扩展点时判断是否是包装类。如果是wrapper包装类,就是调用wrapper的service中的方法,包装后执行。