版本:2.7.5
目录
1.Dubbo SPI
在看Dubbo的SPI之前,先来看看Java的SPI
package com.mingzhi;
public interface Car {
void printCarName();
}
定义一个接口,然后创建如下图的文件,文件名为接口的全包名,内容为接口实现类的全包名
随后main方法中就可以调用ServiceLoader.load去加载实现类
package com.mingzhi;
import java.util.ServiceLoader;
public class Main {
public static void main(String[] args) {
ServiceLoader<Car> serviceLoader = ServiceLoader.load(Car.class);
for (Car car : serviceLoader) {
car.printCarName();
}
}
}
但是这里有一定的局限性,最直接的就是会拿到所有的实现类去实例化,并且如果实现类中还有其他依赖配置,也不能自动注入,于是Dubbo的SPI就在Java SPI的基础上完善了许多。
先来看看Dubbo的SPI基本实现(先下载并导入dubbo源码到ide中)
在META-INF中新建dubbo文件夹
里面内容有一点变化,用=连接了key和value,value任然是实现类的全包名
red=com.mingzhi.RedCar
black=com.mingzhi.BlackCar
接着就能够通过dubbo的SPI获取到特定key的实现类
ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
Car car = extensionLoader.getExtension("black");
System.out.println(car.getCarName());
2.Dubbo Wrapper
dubbo的wrapper能够在执行接口方法之前或者之后添加逻辑,达到类似AOP的功能
首先先定义某个接口的wrapper类,同时也需要实现这个接口
最重要的一点是需要一个此接口的成员变量和构造函数,这样dubbo就将识别此类为wrapper,然后就实现了类似AOP的功能
public class CarWrapper implements Car {
private Car car;
public CarWrapper(Car car) {
this.car = car;
}
@Override
public String getCarName() {
System.out.println("wrapper start");
System.out.println("before");
System.out.println(car.getCarName());
System.out.println("after");
return "wrapper end";
}
}
打印结果符合预期
3.@SPI @Adaptive @Activate
@SPI注解使用在接口上
可以指定value,如果指定了value则表明:如果没有指定具体的实现类(->没有在某个实现类上标上注解@Adaptive)则extensionLoader.getAdaptiveExtension()会使用value的实现类,如果有某个实现类上标上注解@Adaptive,则不管@SPI是否有值都会去加载@Adaptive的实现类
@Adaptive注解使用在实现类或者接口方法上
1.使用在实现类上则表明此实现类为接口的默认实现类,优先级最高
2.使用在接口方法上,则会在初始化的时候生成一个动态代理类,被标记的方法会实现为根据传入的Url参数来执行getExtensionLoader(xx.class).getExtension(name)找到最终执行的那个实现类
对比:类级别省略了生成动态代理类的过程,由指定类决定具体实现,另外对于同一个扩展点,类级别的Adaptive只能有一个
@Activate注解待更新
4.Dubbo SPI核心流程源码分析
首先看dubbo中SPI最常见的获取实现类的方式
ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
进入getExtensionLoader后,可以看到就是去拿接口所对应的extensionLoader,这里可以体现出来一个接口只会有一个扩展加载器
缓存扩展加载器的结构是ConcurrentMap,key是接口类型,value是扩展加载器
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>();
然后看具体去拿扩展实现类的这句代码
Car car = extensionLoader.getExtension("black");
进入getExtension方法后可以看到显示一些判断,如果传进来的为true则去获取默认扩展类
// 获取默认扩展类
if ("true".equals(name)) {
return getDefaultExtension();
}
一直走到org.apache.dubbo.common.extension.ExtensionLoader#cacheDefaultExtensionName去获取默认配置类
后面会去配置文件中读取扩展类
后面就会getExtension去加载配置的默认实现类
如果不为true则会进入下面的逻辑
org.apache.dubbo.common.extension.ExtensionLoader#createExtension
首先会去加载dubbo配置文件中的内容
先看getExtensionClasses方法,会从缓存中先拿,如果没有缓存则去loadExtensionClasses后放入缓存中
/**
* 加载当前ExtensionLoader对象中指定的接口的所有扩展
* @return
*/
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
classes = loadExtensionClasses(); // 加载、解析文件 Map
cachedClasses.set(classes);
}
}
}
return classes;
}
接着会根据拿到的配置中的类型实例出对象然后放入缓存中
创建实例后会进行依赖注入
// 依赖注入 IOC
injectExtension(instance);
里面会从2个地方(Dubbo SPI机制所产生,也可能是从Spring容器中所获得的对象)获取对象后再反射调用setter进行注入
Object object = objectFactory.getExtension(pt, property);
依赖注入ioc之后会进行dubbo的aop也就是包装一层wrapper
首先会先去拿到wrapper的缓存Set(无序所以在有多个wrapper的时候返回的不一定),这个Set是在解析配置文件的时候添加进来的(根据配置文件中类是否有构造方法来判断)
判断是否为wrapper class