0%

springboot的Spi机制

springboot的Spi机制

结合之前说到的springboot的Spi机制本篇将简单介绍一下Doubbo的SPI机制,Dobbo没有使用jdk的这套SPI机制,而是自己实现了一套,提供了一些更灵活、更易于使用的方式。

​ 简单回顾下,SPI全称(Service Provider Interface)是一种服务发现机制,本质上是将接口的实现类的全限定类名写在配置文件中,并由类加载器读取配置文件,这样可以在运行时,动态为接口替换实现类,所以一般用这种方式来为服务提供扩展能力。

JDK的服务发现spi机制

JDK 的 SPI 规范规定:

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

案例

定义一个接口:

1
2
3
public interface CarService {
void run();
}

接口实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ElectricCarServiceImpl implements CarService{

@Override
public void run(){
System.out.println("ElectricCarServiceImpl run");
}

}

public class TankCarServiceImpl implements CarService{

@Override
public void run(){
System.out.println("TankCarServiceImpl run");
}

}

创建配置文件(META-INF/services/cn.com.xiaocainiaoya.spi.jdk.CarService),然后在配置文件中配置接口的实现类的全限定类名:

1
2
cn.com.xiaocainiaoya.spi.jdk.ElectricCarServiceImpl
cn.com.xiaocainiaoya.spi.jdk.TankCarServiceImpl

模拟客户端:

1
2
3
4
5
6
7
8
9
10
11
public class CarMain {
public static void main(String[] args) {
// ServiceLoader.load()方法获取到配置文件中的实现类
ServiceLoader<CarService> loader = ServiceLoader.load(CarService.class);
Iterator<CarService> it = loader.iterator();
while (it.hasNext()) {
CarService service = it.next();
service.run();
}
}
}

最终输出:

1
2
ElectricCarServiceImpl run
TankCarServiceImpl run

这种方式有几个问题:

  • 获取实现类的方式不够灵活,只能通过loader.iterator()迭代器的方式来获取实现类,然后遍历判定当前要使用的实现类,假设说可以以键值对的方式来获取实现类,那么可以增加一定的便利。
  • 不能按需加载,比如某个接口在配置文件中配置的实现类有很多,但是实际在运行过程中只需要其中的小几个,虽然ServiceLoader做了延迟载入,但是获取实现类的手段只能通过遍历来获取,基本上实现类都得加载并实例化。
  • ServiceLoader类的实例是线程不安全。

Dubbo的服务发现机制SPI

Dobbo没有使用Java的这套SPI机制,而是自己实现了一套功能更加强大的SPI机制,Dubbo的SPI是通过ExtensionLoader类来进行实现类加载。

先来简单理解一下Dubbo这套SPI机制中的一些概念:

接口名:可以随意定义,但接口必须被@SPI 注解修饰

实现类名: 在接口名前添加一个用于表示自身功能的“标识前辍”字符串

提供者配置文件路径:在依次查找的目录为

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

提供者配置文件名称:接口的全限定性类名,无需扩展名

提供者配置文件内容: 文件的内容为 key=value 形式, value 为该接口的实现类的全限类性类名, key 可以随意,但一般为该实现类的“标识前辍”(首字母小写)。一个类名占一行。

提供者加载: ExtensionLoader 类相当于 JDK SPI 中的 ServiceLoader 类,用于加载提供者配置文件中所有的实现类,并创建相应的实例。

增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接使用 setter() 方法注入其他扩展点 ,也可 以对扩展点使用 Wrapper 类进行功能增强。

一些名词解释:

  • 普通扩展类:就是接口的具体的实现类(对应JDK的SPI中的具体的实现类)
  • Adaptive类:自适应扩展类,就是类似于在普通扩展类上再包一层,客户端获取到这个自适应扩展类,通过这个扩展类调用定义的扩展接口,它会根据一定的规则(自适应扩展类的接口的参数需要有com.alibaba.dubbo.common.URL)来路由到对应的实现类中。
  • Wrapper类:包装类,对普通扩展类的包装机制,可以对扩展类的SPI接口方法进行增强,一个SPI可以包含多个Wrapper。
  • Activate类:激活类,激活扩展类,可以通过指定的条件来一下子把需要的扩展类激活(因为Dubbo这套机制是按需加载,也就使用到时再进行加载,但是有些场景,可能一下子就要把某一些规则「比如做了分组,要把某个组的类一下子都加载」的类进行加载)

普通扩展类

定义接口:

1
2
3
4
@SPI("car")
public interface CarService {
void run();
}

实现类:

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

@Override
public void run(){
System.out.println("ElectricCarServiceImpl run");
}
}

public class TankCarServiceImpl implements CarService {

@Override
public void run(){
System.out.println("TankCarServiceImpl run");
}
}

创建META-INF/services/cn.com.xiaocainiaoya.spi.dubbo.CarService文件,并添加配置:

注:这里与JDK的SPI稍有不同,这是是自定为每个接口实现定义了一个key。

1
2
electric=cn.com.xiaocainiaoya.spi.dubbo.ElectricCarServiceImpl
tank=cn.com.xiaocainiaoya.spi.dubbo.TankCarServiceImpl

客户端:

1
2
3
4
5
6
7
8
9
public class CarMain {
public static void main(String[] args) {
// 获取到用于加载Order类型扩展类实例的extensionLoader实例
ExtensionLoader<CarService> loader = ExtensionLoader.getExtensionLoader(CarService.class);
// 这里只会获取到electric这个key指向的实例,它也只会加载这个实例,达到了按需加载的效果
CarService car = loader.getExtension("electric");
car.run();
}
}

自适应扩展类Adaptive

自适应扩展@Adaptive注解可以标记在类上,也可以标记在方法上。

1.标记在类上

定义接口:

1
2
3
4
5
6
7
@SPI("electric")
public interface CarService {

void display();

void run();
}

实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class ElectricCarServiceImpl implements CarService {

@Override
public void display() {
System.out.println("ElectricCarServiceImpl display");
}

@Override
public void run(){
System.out.println("ElectricCarServiceImpl run");
}
}

public class TankCarServiceImpl implements CarService {

@Override
public void display() {
System.out.println("TankCarServiceImpl display");
}

@Override
public void run(){
System.out.println("TankCarServiceImpl run");
}
}

// 标记在类上
@Adaptive
public class AdaptiveCarServiceImpl implements CarService {

private String defaultName;

// 指定要加载扩展类的名称
public void setDefaultName(String defaultName) {
this.defaultName = defaultName;
}

@Override
public void display() {
ExtensionLoader<CarService> loader = ExtensionLoader.getExtensionLoader(CarService.class);
CarService carService;
if (StringUtils.isEmpty(defaultName)) {
// 加载SPI默认名称的扩展类
carService = loader.getDefaultExtension();
} else {
// 加载指定名称的扩展类
carService = loader.getExtension(defaultName);
}
carService.display();
}
}

配置文件:

注意一个@SPI接口只能有一个标记@Adaptive在类上的实现,所以在有这个要求的情况下,这个类的在下面配置文件中的key就显得无关紧要,这里是叫adaptive,你可以叫其他名字。

那么假设某个接口有两个标记了@Adaptive类,并且两个类都写在了下方的配置文件中会怎么样?

ExtensionLoader#loadFile()方法中逐行扫描这些配置文件时,会发现某个@SPI接口有两个@Adaptive的实现类,后一个扫描到时会中断,之后它的做法是把异常信息保存在Map<String, IllegalStateException> exceptions,并不会使得程序都中断。也就是说假设有两个@Apdative的情况下,最终加载的是靠前的那个实现。

1
2
3
electric=cn.com.xiaocainiaoya.spi.dubbo.adaptive.ElectricCarServiceImpl
tank=cn.com.xiaocainiaoya.spi.dubbo.adaptive.TankCarServiceImpl
adaptive=cn.com.xiaocainiaoya.spi.dubbo.adaptive.AdaptiveCarServiceImpl

客户端:

1
2
3
4
5
6
7
8
9
10
11
public class CarMain {
public static void main(String[] args) {
ExtensionLoader<CarService> loader = ExtensionLoader.getExtensionLoader(CarService.class);
CarService carService = loader.getAdaptiveExtension();
((AdaptiveCarServiceImpl)carService).setDefaultName("tank");
carService.display();
// 这里要注意,自适应扩展类不属于直接扩展类!!!!
Set<String> supportedExtensions = loader.getSupportedExtensions();
System.out.println(supportedExtensions);
}
}

输出:

1
2
TankCarServiceImpl display
[electric, tank]

2.标记在方法上

定义接口:

1
2
3
4
5
6
7
8
9
@SPI("electric")
public interface CarService {

void display();

@Adaptive
void run(URL url);

}

实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class ElectricCarServiceImpl implements CarService {

@Override
public void display() {
System.out.println("ElectricCarServiceImpl display");
}

@Override
public void run(URL url){
System.out.println("ElectricCarServiceImpl run");
}
}

public class TankCarServiceImpl implements CarService {

@Override
public void display() {
System.out.println("TankCarServiceImpl display");
}

@Override
public void run(URL url){
System.out.println("TankCarServiceImpl run");
}
}

@Adaptive标记在方法上时,会通过javassist为该接口生成一个动态类CarService$Adaptive,这个自动生成的代理类的主要作用是根据URL这个类来传入路由规则,使得此类可以根据这个路由规则,找到具体的实现类。(这个类是动态生成的,这就导致了不能通过IDE打断点之类的方式进行调试)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package cn.com.xiaocainiaoya.spi.dubbo.adaptive;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class CarService$Adaptive implements cn.com.xiaocainiaoya.spi.dubbo.adaptive.CarService {
public void display() {
throw new UnsupportedOperationException("method public abstract void cn.com.xiaocainiaoya.spi.dubbo.adaptive" +
".CarService.display() of interface cn.com.xiaocainiaoya.spi.dubbo.adaptive.CarService is not " +
"adaptive method!");
}

public void run(com.alibaba.dubbo.common.URL arg0) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("car.service", "electric");
if (extName == null)
throw new IllegalStateException("Fail to get extension(cn.com.xiaocainiaoya.spi.dubbo.adaptive" +
".CarService) name from url(" + url.toString() + ") use keys([car.service])");
cn.com.xiaocainiaoya.spi.dubbo.adaptive.CarService extension = (cn.com.xiaocainiaoya.spi.dubbo.adaptive.CarService)
ExtensionLoader.getExtensionLoader(cn.com.xiaocainiaoya.spi.dubbo.adaptive.CarService.class).getExtension(extName);
extension.run(arg0);
}
}

客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class CarMain {
public static void main(String[] args) {
// 获取到用于加载Order类型扩展类实例的extensionLoader实例
ExtensionLoader<CarService> loader = ExtensionLoader.getExtensionLoader(CarService.class);
// 获取自适应扩展类,它是一个代理类
CarService car = loader.getAdaptiveExtension();
// 不传参数,它会去查找@SPI("car") 注解,获取默认值
URL url = URL.valueOf("xxx");
car.run(url);
// 传参,注意这里的参数名要和@SPI的接口名把驼峰转换为点号分隔作为参数名
URL url2 = URL.valueOf("xxx?car.service=tank");
car.run(url2);
// 这里是不能调用display方法,它不是自适应扩展类的方法,根据动态生成的类CarService$Adaptive可以看出来,
// display方法的实现里直接抛出了UnsupportedOperationException异常
car.display();
}
}

执行结果:

1
2
ElectricCarServiceImpl run
TankCarServiceImpl run

3.小结

​ 对比两种对于@Adaptive标记的位置的不同,可以发现,其实标记在类上,就是需要自己去实现路由规则,由自己来控制怎么找到自己需要的实现类;标记在接口上,是Dubbo通过做了一层高度抽象,配合URL类和根据javassist动态生成自适应扩展类,来路由到具体需要使用到的实现类。

包装机制Wrapper

定义接口:

1
2
3
4
5
6
7
8
@SPI("electric")
public interface CarService {

void display();

@Adaptive
void run(URL url);
}

实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class ElectricCarServiceImpl implements CarService {

@Override
public void display() {
System.out.println("ElectricCarServiceImpl display");
}

@Override
public void run(URL url){
System.out.println("ElectricCarServiceImpl run");
}

}

public class TankCarServiceImpl implements CarService {

@Override
public void display() {
System.out.println("TankCarServiceImpl display");
}

@Override
public void run(URL url){
System.out.println("TankCarServiceImpl run");
}

}

包装类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class CarServiceWrapper implements CarService {
private CarService carService;

public CarServiceWrapper(CarService carService) {
this.carService = carService;
}

@Override
public void display() {
System.out.println("CarServiceWrapper display before");
carService.display();
System.out.println("CarServiceWrapper display after");
}

@Override
public void run(URL url){
System.out.println("CarServiceWrapper run before");
carService.run(url);
System.out.println("CarServiceWrapper run after");
}
}

public class CarServiceWrapper2 implements CarService {
private CarService carService;

public CarServiceWrapper2(CarService carService) {
this.carService = carService;
}

@Override
public void display() {
System.out.println("CarServiceWrapper2 display before");
carService.display();
System.out.println("CarServiceWrapper2 display after");
}

@Override
public void run(URL url){
System.out.println("CarServiceWrapper2 run before");
carService.run(url);
System.out.println("CarServiceWrapper2 run after");
}
}

配置类:

1
2
3
4
electric=cn.com.xiaocainiaoya.spi.dubbo.adaptive.ElectricCarServiceImpl
tank=cn.com.xiaocainiaoya.spi.dubbo.adaptive.TankCarServiceImpl
wrapper=cn.com.xiaocainiaoya.spi.dubbo.adaptive.CarServiceWrapper
wrapper2=cn.com.xiaocainiaoya.spi.dubbo.adaptive.CarServiceWrapper2

客户端:

1
2
3
4
5
6
7
8
9
10
11
public class CarMain {
public static void main(String[] args) {
// 获取到用于加载Order类型扩展类实例的extensionLoader实例
ExtensionLoader<CarService> loader = ExtensionLoader.getExtensionLoader(CarService.class);
// 获取自适应扩展类,它是一个代理类
CarService car = loader.getAdaptiveExtension();
// 不传参数,它会去查找@SPI("car") 注解,获取默认值
URL url = URL.valueOf("xxx");
car.run(url);
}
}

输出:

1
2
3
4
5
CarServiceWrapper2 run before
CarServiceWrapper run before
ElectricCarServiceImpl run
CarServiceWrapper run after
CarServiceWrapper2 run after

激活机制Activate

用于直接激活扩展类,通过一定的规则(分组)等方式,一下子激活符合条件的扩展类。

1
2
3
4
5
6
7
8
9
10
11
12
public @interface Activate {

String[] group() default {};

String[] value() default {};

String[] before() default {};

String[] after() default {};

int order() default 0;
}

before()after()已经过时。

  • group:分组名称,根据组名称来激活一些类(是一个大范围,指定到一个集体)
  • value:指定一个名称,可以不根据分组,直接根据名称来激活(一个小范围,直接指定到个体)
  • order:排序号,序号越小,优先级越高,默认值为0。

定义接口:

1
2
3
4
5
6
7
8
@SPI("electric")
public interface CarService {

void display();

void run(URL url);

}

实现类:

3个具体实现类,其中HybridCarServiceImpl既属于tankGroup,也属于electricGroup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Activate(group = "electricGroup")
public class ElectricCarServiceImpl implements CarService {

@Override
public void display() {
System.out.println("ElectricCarServiceImpl display");
}

@Override
public void run(URL url){
System.out.println("ElectricCarServiceImpl run");
}
}

@Activate(group = {"tankGroup", "electricGroup"})
public class HybridCarServiceImpl implements CarService {

@Override
public void display() {
System.out.println("HybridCarServiceImpl display");
}

@Override
public void run(URL url){
System.out.println("HybridCarServiceImpl run");
}
}

@Activate(group = "tankGroup")
public class TankCarServiceImpl implements CarService {

@Override
public void display() {
System.out.println("TankCarServiceImpl display");
}

@Override
public void run(URL url){
System.out.println("TankCarServiceImpl run");
}
}

配置:

1
2
3
electric=cn.com.xiaocainiaoya.spi.dubbo.adaptive.ElectricCarServiceImpl
tank=cn.com.xiaocainiaoya.spi.dubbo.adaptive.TankCarServiceImpl
hybrid=cn.com.xiaocainiaoya.spi.dubbo.adaptive.HybridCarServiceImpl

客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void main(String[] args) {

ExtensionLoader<CarService> loader = ExtensionLoader.getExtensionLoader(CarService.class);
URL url = URL.valueOf("xxx");

// 激活整个组,返回整个组的实现类列表
List<CarService> tankServices = loader.getActivateExtension(url, "", "tankGroup");

// 激活整个组,但是只会返回第二个参数指定的实现类
List<CarService> tankServices = loader.getActivateExtension(url, "tank", "tankGroup");

// 激活整个组,但是只会返回URL对象中的参数key的实现类
URL url = URL.valueOf("xxx?tank=true");
List<CarService> tankServices = loader.getActivateExtension(url, "", "tankGroup");


// 综上:其实不论是为了获取那些实现类,实际上都是激活了(加载了)整个组的实现类

for (CarService carService : tankServices) {
carService.run(url);
}

}
}

输出:

1
2
TankCarServiceImpl run
HybridCarServiceImpl run

注:我在@Activate中只有指定了group值,并没有指定value

1
2
3
4
5
6
7
8
9
10
11
12
// 经过我的测试,如果我的@Activate是这样声明:
@Activate(group = "tankGroup", value = "tank")
@Activate(group = {"tankGroup", "electricGroup"}, value = "hybrid")
@Activate(group = "electricGroup" , value = "electric")

// 执行后输出的结果是空的,是一个实现类都获取不到的,我看有些博客是说这个语句的后两个参数是或的关系
// 经过我的测试,并不是这样。我的dobbo版本是2.6.0。
List<CarService> tankServices = loader.getActivateExtension(url, "", "tankGroup");

// 通过这种方式获取tankGroup中value值为tank的实现类
URL url = URL.valueOf("xxx?service.car=tank");
List<CarService> tankServices = loader.getActivateExtension(url, "service.car", "tankGroup");

看看源码

根据前面提到的特性,我们需要通过一个类,来自动查找具体实现类,这个类就是ExtensionLoader(在jdk中是通过ServiceLoader)。

这是一个静态方法,通过这个方法获取ExtensionLoader实例。

1
ExtensionLoader<Car> loader = ExtensionLoader.getExtensionLoader(Car.class);

先从缓存中获取,如果获取不到,则创建一个新的ExtensionLoader实例

1
2
3
4
5
6
7
8
9
10
11
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
// ... 省略

// EXTENSION_LOADERS中获取,如果获取不到则创建一个
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;
}

来看一下这个类的构建函数:

1
2
3
4
5
6
7
8
9
private ExtensionLoader(Class<?> type) {
// 当前加载类的类型
this.type = type;
// 判断当前需要创建的类是不是ExtensionFactory类型,ExtensionFactory是dubbo内置的一个@SPI接口
// 它的具体实现类有SpiExtensionFactory和SpringExtensionFactory,它的自适应扩展类是AdaptiveExtensionFactory(@Adaptive标记在类上)
// 看下这个语句,如果type不是ExtensionFactory,那么ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()
// 也就是,除了它自己,其他类型的ExtensionLoader实例的objectFactory(对象创建工厂)都是ExtensionFactory这个类的自适应扩展类
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

ExtensionFactory的两个实现:从以下可以看出,这是为了兼容spring,加载具体实现类可以从spring上下文中获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class SpringExtensionFactory implements ExtensionFactory {

private static final Set<ApplicationContext> contexts = new ConcurrentHashSet<ApplicationContext>();

@SuppressWarnings("unchecked")
public <T> T getExtension(Class<T> type, String name) {
// 从spring容器中获取bean
for (ApplicationContext context : contexts) {
if (context.containsBean(name)) {
Object bean = context.getBean(name);
if (type.isInstance(bean)) {
return (T) bean;
}
}
}
return null;
}
}

拿到ExtensionLoader实例之后,就可以执行上文中介绍的一些特性功能。

1
2
3
4
5
6
7
8
// 直接获取对应key指向的实现类
CarService car = loader.getExtension("electric");

// 获取自适应扩展类,通过自适应扩展类来路由到具体实现类的具体方法
CarService car = loader.getAdaptiveExtension();

// 直接激活所有符合条件的扩展类
loader.getActivateExtension(url, "", "tankGroup");

具体实现类的获取过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public T getExtension(String 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);
}
// 双重检测锁用来防止并发冲突,在ExtensionLoader中大量使用
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
// 创建实现类
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}

创建扩展类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private T createExtension(String name) {
// getExtensionClasses()主要的作用是从配置中获取到@SPI标记的那个接口的实现类,并将相关信息(全限定类名)进行缓存
// 将需要使用到的类进行加载并返回到这里
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
// 如果不存在,则加入缓存中
EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// 对这个实现进行注入
injectExtension(instance);
// 获取到包装类的缓存
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && wrapperClasses.size() > 0) {
for (Class<?> wrapperClass : wrapperClasses) {
// 对这个实现进行包装,获取到包装类以当前类型为参数的构造函数,然后调用newInstance创建实例(包装类必须为@SPI接口的实现)
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
// 最终就返回了一个经过注入和经过包装的具体实现类的代理类(如果没有包装,就不是代理类)
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
type + ") could not be instantiated: " + t.getMessage(), t);
}
}

加载扩展类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}

private Map<String, Class<?>> loadExtensionClasses() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
String value = defaultAnnotation.value();
if (value != null && (value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
if (names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
if (names.length == 1) cachedDefaultName = names[0];
}
}

Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
// 加载对应路径下的文件内容:META-INF/dubbo/internal、META-INF/dubbo、META-INF/services
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
// loadFile()方法就不贴了,主要做的事情就是:
// 1. 是按key=value的方式逐行读取文件内容
// 2. 如果读取的类标记了@Adaptive,则表示是自适应扩展类,赋值到 cachedAdaptiveClass,并且一个@SPI只能有一个@Adaptive扩展类
// 3. 如果读取的类标记了@Activate,则表示需要进行自动激活,则加入到 Map<String, Activate> cachedActivates

return extensionClasses;
}

扩展类注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private T injectExtension(T instance) {
try {
// 对象工厂不为空,这里在构造函数中创建了ExtensionFactory的自适应扩展类
if (objectFactory != null) {
// 遍历所有方法
for (Method method : instance.getClass().getMethods()) {
// 1. 方法名需要时set开头 2. 方法需要是public方法 3. 方法参数只有1个参数
if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers())) {
Class<?> pt = method.getParameterTypes()[0];
try {
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
// 通过对象工厂获取对象,这里自适应扩展类会使得SpiExtensionFactory和SpringExtensionFactory都执行
// 也就是说这里会从spring容器中获取,如果是从SpiExtensionFactory,拿到的是pt的自适应扩展类
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("fail to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}

自适应扩展类

首先来看看自适应扩展类的相关源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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);
}
// ... 省略部分代码
}
}
}
}
return (T) instance;
}

创建自适应扩展类:

1
2
3
4
5
6
7
8
private T createAdaptiveExtension() {
try {
// 先获取到自适应扩展类的实例,再为自适应扩展类注入需要的对象
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}

获取到自适应扩展类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private Class<?> getAdaptiveExtensionClass() {
// 这个方法其实前面已经介绍过了,如果 cachedClasses 里没有,则会去加载指定目录(META-INF/XXX)
// 下的配置文件,并将对应的类加载
getExtensionClasses();
// 如果 cachedAdaptiveClass 不为空,则表示这个ExtensionLoader实例的自适应扩展类已经加载过了
// 什么情况到这里会加载过呢?
// 就是为这个@SPI标记的接口,指定了一个类级的@Adaptive,也就是说将@Adaptive标记在类上
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 到这里就是标记方法级的@Adaptive,这里需要进行动态生成代码并编译
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

private Class<?> createAdaptiveExtensionClass() {
// 创建动态生成的文件字符串,其实就是通过StringBuilder然后各种判断后拼接字符串,创建出一个xxx$Adaptive类的字符串
String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();
// 使用编译器编译这个类的字符串
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}

我这边测试的时候动态生成的一个自适应扩展类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package cn.com.xiaocainiaoya.spi.dubbo.adaptive;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class CarService$Adaptive implements cn.com.xiaocainiaoya.spi.dubbo.adaptive.CarService {
// 没有标记@Adaptive注解的方法是不允许通过自适应扩展类访问
public void display() {
throw new UnsupportedOperationException("method public abstract void cn.com.xiaocainiaoya.spi.dubbo.adaptive" +
".CarService.display() of interface cn.com.xiaocainiaoya.spi.dubbo.adaptive.CarService is not " +
"adaptive method!");
}

// 被标记@Adaptive注解的方法,通过URL设置了一些参数规则,根据这个参数规则,获取到对应的参数来路由到对应的实现类
public void run(com.alibaba.dubbo.common.URL arg0) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("car.service", "electric");
if (extName == null)
throw new IllegalStateException("Fail to get extension(cn.com.xiaocainiaoya.spi.dubbo.adaptive" +
".CarService) name from url(" + url.toString() + ") use keys([car.service])");
cn.com.xiaocainiaoya.spi.dubbo.adaptive.CarService extension = (cn.com.xiaocainiaoya.spi.dubbo.adaptive.CarService)
ExtensionLoader.getExtensionLoader(cn.com.xiaocainiaoya.spi.dubbo.adaptive.CarService.class).getExtension(extName);
extension.run(arg0);
}
}

自动激活机制

1
2
3
4
5
6
7
ExtensionLoader<CarService> loader = ExtensionLoader.getExtensionLoader(CarService.class);
URL url = URL.valueOf("xxx");

List<CarService> tankServices = loader.getActivateExtension(url, "", "tankGroup");
for (CarService carService : tankServices) {
carService.run(url);
}

获取需要自动激活的实现类:

1
2
3
4
public List<T> getActivateExtension(URL url, String key, String group) {
String value = url.getParameter(key);
return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group);
}

获取自动激活的实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> exts = new ArrayList<T>();
List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
//
if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
// 加载这个@SPI接口对应的相关信息「去加载META-INF文件夹下的文件」
getExtensionClasses();
for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
String name = entry.getKey();
Activate activate = entry.getValue();
// 组是否对应
if (isMatchGroup(group, activate.group())) {
// 获取对应的扩展类,到这一步其实就已经在加载名为name的这个类了
T ext = getExtension(name);
// 1. 如果当前循环的这个name不落在names中时(因为names是直接指定,优先级比较低,是在下方的那个for循环中加入到exts结果列表中)
// 2. 如果names中不包含“-name”时(这个目前不太清楚是干嘛用的,可能是有一些特殊的操作判断)
// 3. isActive()是否符合激活规则(url参数中指定的规则)
if (!names.contains(name) && !names.contains(Constants.REMOVE_VALUE_PREFIX + name) && isActive(activate, url)) {
exts.add(ext);
}
}
}
Collections.sort(exts, ActivateComparator.COMPARATOR);
}
List<T> usrs = new ArrayList<T>();
// 用户直接指定的实现,比如这种写法 loader.getActivateExtension(url, new String[]{"tank", "electric"});
// 就是直接激活@Activate中value=tank和value=electric的扩展类
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX)
&& !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
if (Constants.DEFAULT_KEY.equals(name)) {
if (usrs.size() > 0) {
exts.addAll(0, usrs);
usrs.clear();
}
} else {
T ext = getExtension(name);
usrs.add(ext);
}
}
}
if (usrs.size() > 0) {
exts.addAll(usrs);
}
return exts;
}

目前按我理解:自动激活机制的意思是,会将整个组都激活(加载),但是接口会根据相应的规则来获取对应的扩展类。(比如groupA下有V1,V2,V3,groupB下有W1,W2,W3,如果是加载groupA,那么V1,V2,V3都会激活,但是会根据一些规则判断,返回其中的某个或者某些,比如只返回V1,或者返回V1,V2)

-------------本文结束感谢您的阅读-------------