dubbo学习-SPI机制
Posted ljming的博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dubbo学习-SPI机制相关的知识,希望对你有一定的参考价值。
1. 什么是SPI
SPI全称为Service Provider Interface,是一种服务发现机制。SPI的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因为次特性,可以很容易的通过SPI机制为程序提供扩展功能。
SPI中有两个角色,一个称之为扩展点,另一个为扩展点实现。
扩展点:通过spi机制查找并加载实现的接口
扩展点实现:实现了扩展接口的实现类。
2. SPI示例
在了解dubbo spi之前,先看看Java的spi是怎么使用的。创建一个java spi程序的具体步骤如下:
定义一个接口及对应的方法
编写该接口的一个实现类
在
META-INF/services/
目录下,创建一个以接口全路径名命名的文件,如top.ljming.dubboprovider.spi.java.Car文件内容为具体实现类的全路径名,如果有多个,使用换行符分割。
使用java.util.ServiceLoader来在加载具体的实现类。
// top.ljming.dubboprovider.spi.java.Car文件内容
top.ljming.dubboprovider.spi.java.SportCar
top.ljming.dubboprovider.spi.java.BusinessCar
// 定义一个Car类的接口和run方法
public interface Car {
void run();
}
// SportCar实现car类,并将run方法实现
public class SportCar implements Car {
@Override
public void run() {
System.out.println("sport car running fast");
}
}
// BusinessCar实现car类,并将run方法实现
public class BusinessCar implements Car {
@Override
public void run() {
System.out.println("business car running steady");
}
}
// 测试方法类
public void testSpi() {
ServiceLoader<Car> carServiceLoader = ServiceLoader.load(Car.class);
for (Car car : carServiceLoader) {
car.run();
}
}
// 输出
sport car running fast
business car running steady
接下来,我们使用Dubbo spi来对上面的接口和扩展类进行改造。具体修改如下:
在
META-INF/dubbo/
目录下,同样创建一个以接口全路径名命名的文件,如top.ljming.dubboprovider.spi.java.Car文件内容kv格式,k为具体实现类的表示,v是具体实现类的全路径名,如有多个,换行符分割。
使用org.apache.dubbo.common.extension.ExtensionLoader加载特定的实现类。
// top.ljming.dubboprovider.spi.java.Car文件内容
sport=top.ljming.dubboprovider.spi.java.SportCar
business=top.ljming.dubboprovider.spi.java.BusinessCar
// 改造Car接口
@SPI("sport") // 添加SPI注解,表示Car的默认实现为sport
public interface Car {
void run();
}
// 测试方法
@Test
public void testDubboSpi() {
ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
Car sportCar = extensionLoader.getDefaultExtension();
sportCar.run();
}
// 输出
sport car running fast
相比Java spi,dubbo spi做了一定的改进和优化。
java spi 会一次性加载所有扩展点实现,如果有扩展实现则初始化很耗时,如果没用上也加载,则浪费资源。
增加了对扩展IOC和AOP的支持,一个扩展可以直接setter注入其它扩展。在java spi中,会一次把所有相关的扩展实现类全部初始化,用户直接调用即可。在dubbo spi中,只是加载配置文件中的类,并分成不同的种类缓存在内存中,而不会立即全部初始化,在性能上有更好的表现。
3. Dubbo SPI
dubbo spi机制,在jdk的spi基础上实现了自身的一套机制,不仅解决了上述提到的资源浪费问题,还对spi配置文件进行了扩展和修改。
在dubbo启动的时候,会默认扫描一下三个目录的配置文件:
META-INF/services/目录:用于兼容jdk spi
META-INF/dubbo/目录:存放用户自定义spi配置文件
META-INF/dubbo/internal/目录:存放dubbo内部使用的spi配置文件
dubbo spi配置文件是以kv的形式,例如:
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
k是扩展名,v是具体的扩展实现类。
3.1 扩展点的特征
扩展类一共包含四种特性:自动包装、自动加载、自适应和自动激活。
自动包装
扩展类在被加载时,如果发现这个扩展类包含其它扩展点作为构造函数的参数,则这个扩展类就会被认为是Wrapper类。
自动加载
除了在构造函数中传入其它扩展实例,我们还经常使用setter方法设置属性值。如果某个扩展类是另外一个扩展点类的成员属性,并且拥有setter方法,那么框架也会自动注入对应的扩展点实例。
自适应
在dubbo spi中,使用@Adaptive注解,可以动态的通过url中的参数来确定要使用那个具体的实现类。从而解决自动加载中的实例注入问题。
自动激活
使用@Active注解,可以标记对应的扩展点默认被激活启用。该注解还可以通过传入不同的参数,设置扩展点在不同的条件下被自动激活。主要使用场景是某个扩展点的多个实现类需要同时启用(如Filter扩展点)。
3.2 扩展点相关注解
扩展点相关注解包括:@SPI、@Adaptive、@Active。
3.2.1 扩展点注解:@SPI
dubbo中,某个接口被@SPI注解修饰时,表示该接口是一个扩展接口。
3.2.2 扩展点自适应注解: @Adaptive
该注解可以标记在类、接口、枚举和方法上。如果标注在接口的方法上,则可以通过参数动态获得实现类。方法级别注解在第一次getExtension时,会自动生成和编译一个动态的Adaptive类,从而达到动态实现类的效果。当该注解在实现类上,那么该实现类会直接作为默认实现,不再自动动态生成Adaptive类。在扩展点接口的多个实现中,只能有一个实现类上可以加@Adaptive注解,如果多个实现类有该注解,则会抛出异常。
3.2.3 扩展点自动激活注解: @Active
该注解可以标记在类、接口、枚举和方法上。主要使用在有多个扩展点实现、需要根据不同条件被激活的场景中,如Filter需要多个被同时激活,因为每个Filter实现的是不同的功能。
3.3 ExtensionLoader工作原理
ExtensionLoader是整个扩展机制的主要逻辑类。在了解其工作原理之前,需要了解它的几个重要的成员属性。
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);
private final Class<?> type;
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>();
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
private volatile Class<?> cachedAdaptiveClass = null;
private Set<Class<?>> cachedWrapperClasses;
EXTENSION_LOADERS: dubbo中一个扩展接口对应一个ExtensionLoader实例,该集合缓存了全部ExtensionLoader实例。key为扩展接口,value为加载其扩展实现的ExtensionLoader实例。
EXTENSION_INSTANCES : 该集合中缓存了扩展实现类与其实例对象的映射关系。例如 org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol 对应 DubboProtocol对象
type: 当前ExtensionLoader实例负责加载扩展接口
cachedNames: 缓存了该ExtensionLoader加载的扩展实现类与扩展名之间的映射关系
cachedClasses: 缓存了该ExtensionLoader加载的扩展名与扩展实现类之间的映射关系。cacheNames集合的反向关系缓存
cachedActivates: 缓存了扩展名与自动激活的类
cachedInstances: 缓存了该ExtensionLoader加载的扩展名与扩展实现对象之间的映射关系
cachedAdaptiveInstance: 实例化后的自适应扩展对象,一个扩展点只能同时存在一个
cacheAdaptiveClass: 自适应扩展类缓存
cachedWrapperClasses: 包装类缓存
那么就从下面这段测试代码开始,深入理解dubbo spi的实现原理。
public void testDubboSpi() {
ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
Car sportCar = extensionLoader.getExtension("sport");
sportCar.run();
Car businessCar = extensionLoader.getExtension("business");
businessCar.run();
}
测试代码做了两件事,获取扩展类的ExtensionLoader实例,然后通过该实例获取对应的扩展类。
3.3.1 getExtensionLoader(Class<T> type)实现
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
/**
省略参数校验代码
**/
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); // 1
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); // 2
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); // 3
}
return loader;
}
第1步: 首先从缓存了所有ExtensionLoader实例缓存EXTENSION_LOADER获取,缓存中存在则返回,否则执行2,3。
第2步: 缓存中没有对应实例,那就通过new一个实例,并将实例放入EXTENSION_LOADERS缓存。
第3步: 返回第二步new出来的实例。
看下ExtensionLoader的构造函数实现:
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
首先将扩展类和ExtensionLoader实例绑定;然后判断当前扩展类是否是ExtensionFactory,如果不是,就会创建ExtensionFactory类的ExtensionLoader实例,进而获取ExtensionFactory的Adaptive(自适应)实现类,赋值给objectFacotry。如果是那么objectFactory则为null。
objectFactory是ExtensionFactory类型,ExtensionFactory也是一个扩展接口,该接口值定义了一个getExtension方法,用于返回扩展类实例。
@SPI
public interface ExtensionFactory {
<T> T getExtension(Class<T> type, String name);
}
ExtensionFactory有三个扩展实现类,分别是AdaptiveExtensionFactory、SpiExtensionFactory、SpringExtensionFactory。AdaptiveExtensionFactory扩展实现类被@Adaptive修饰,作为ExtensionFactory的自适应实现。SpiExtensionFactory是基于dubbo的spi实现。SpringExtensionFactory基于spring容器,根据名称从spring容器中获取对应的实例。
AdaptiveExtensionFactory
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
private final List<ExtensionFactory> factories;
public AdaptiveExtensionFactory() {
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
for (String name : loader.getSupportedExtensions()) {
list.add(loader.getExtension(name));
}
factories = Collections.unmodifiableList(list);
}
@Override
public <T> T getExtension(Class<T> type, String name) {
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
}
AdaptiveExtensionFactory作为ExtensionFactory的自适应实现,它的getExtension方法实现,依赖于其它另外两个实现类。在它的构造函数中,会将另外两种实现类放入factories中,最终的getExtension方法会调用另外两种实现类的getExtension方法。
SpiExtensionFactory
public class SpiExtensionFactory implements ExtensionFactory {
@Override
public <T> T getExtension(Class<T> type, String name) {
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
if (!loader.getSupportedExtensions().isEmpty()) {
return loader.getAdaptiveExtension();
}
}
return null;
}
}
通过SpiExtensionFactory加载的扩展实现类,必须是一个接口而且需要被@SPI注解修饰。最终返回扩展接口的自适应实现类。
SpringExtensionFactory
public class SpringExtensionFactory implements ExtensionFactory {
private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>();
/**省略无关代码**/
@Override
public <T> T getExtension(Class<T> type, String name) {
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
return null;
}
for (ApplicationContext context : CONTEXTS) {
T bean = BeanFactoryUtils.getOptionalBean(context, name, type);
if (bean != null) {
return bean;
}
}
return null;
}
}
SpringExtensionFactory的getExtension方法实现,首先也会判断要加载的类必须是接口,而且被@SP注解修饰。然后从spring的容器中通过名称和类型去查找对应的bean。
3.3.2 getExtension(String name) 实现
通过调用ExtensionLoader的getExtensionLoader方法获取到扩展接口的ExtensionLoader实例之后,就可以通过调用该实例的getExtension方法通过扩展名称获取到对应的扩展实现类。
public T getExtension(String name) {
if ("true".equals(name)) { // ①
return getDefaultExtension();
}
final Holder<Object> holder = getOrCreateHolder(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;
}
①: 判断传过来的name是否为true,如果为true的话,表示的是获取默认扩展实现类,也就是@SPI括号里写的名称。如:
@SPI("dubbo")
public interface Protocol {
}
// 这就表示Protocol的默认扩展实现类就是DubboProtocol
②: 获取目标实例的缓存对象,holder实例会持有目标实例,和目标对象一一对应。getOrCreateHolder方法实现也是首先从cachedInstances缓存中获取,如果没有获取到,则创建一个Holder对象,并放入缓存,返回创建的对象。
③: 先从目标实例的缓存对象holder中尝试获取目标实例,如果为空,则进行创建。反之,直接返回目标实例。创建目标实例的过程,使用了double-check方式,确保目标实例是单例对象。
④: 经过double-check之后,instance还为null,那么执行创建流程。
⑤: 将创建好的instance实例放入缓存对象holder中。
3.3.3 createExtension(String name) 实现
private T createExtension(String name) {
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, clazz.newInstance()); // ③
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
injectExtension(instance); // ④
Set<Class<?>> wrapperClasses = cachedWrapperClasses; // ⑤
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); // ⑥
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
①: 根据name获取对应的扩展实现类class,getExtensionClasses返回的是一个Map<String, Class<?>>。
②: 获取到对应的class之后,会从扩展实现类实例缓存EXTENSION_INSTANCES中根据class获取实例,赋值给instance。
③: 如果instance为空,也就是说从EXTENSION_INSTANCES缓存中没获取到实例,那就会通过反射创建一个实例。并放入到EXTENSION_INSTANCES缓存。
④: 注入实例依赖的扩展点实例。injectExtension方法会扫描所以的setter方法,根据setter方法名称和参数类型,加载响应的扩展点实例,并通过setter方法进行属性填充。这里就展现了扩展点的自动加载特性。
⑤: warpperClasses是一个set集合,保存了扩展点实例的全部包装类。这里使用了装饰器模式,通过扩展点实现类的包装类,对扩展点实现类功能做了增强。这展示了扩展点的自动包装特性。
⑥: 根据包装类的特性:包装类会持有被包装类的引用,创建包装类实例之后,需要调用injectExtension方法为包装类注入被包装类的实例。
3.3.4 getExtensionClasses()实现
该方法用于加载扩展接口的所有实现类class,主要会加载前面说到的dubbo spi的几个配置文件内容。
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;
}
这个方法内容也简单明了,首先从cachedClasses缓存中获取扩展接口的所有扩展实现类的名称和class映射。如果cacheClasses中没有,那就创建,通用使用double-check的方法来实现并发控制。具体如何加载扩展接口的扩展实现类的class,需要看loadExtensionClasses方法。
private Map<String, Class<?>> loadExtensionClasses() {
cacheDefaultExtensionName(); // ①
Map<String, Class<?>> extensionClasses = new HashMap<>();
// ②
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
return extensionClasses;
}
①: 首先会调用cacheDefaultExtensionName方法,缓存默认的扩展实现类,也就是给cachedDefaultName赋值。具体实现: 先判断当前加载的扩展点是否被@SPI注册修饰,如果没有,则不作任何操作;反之,会获取注解的value,并对value进行分割获取到一个value数组,如果数组长度超过1,则抛出异常,因为一个扩展接口只能有一个默认扩展实现。最后将数组的第一个valu值赋值给cachedDefaultName。
②: 加载对应的文件夹下的spi配置文件,填充extensionClasses。
loadDirectory方法的实现,主要是循环加载对应文件夹下的文件,通过调用loadResource方法读取文件内容。
loadResource方法的实现,按行读取文件内容,根据=
分割获取k-v对,k为扩展实现类名称,v为扩展实现类全限定名。再调用loadClass加载扩展接口的自适应class、包装类class、自动激活class等。
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) {
if (clazz.isAnnotationPresent(Adaptive.class)) {
cacheAdaptiveClass(clazz); // ①
} else if (isWrapperClass(clazz)) {
cacheWrapperClass(clazz); // ②
} else {
clazz.getConstructor(); // ③
if (StringUtils.isEmpty(name)) {
name = findAnnotationName(clazz);
if (name.length() == 0) {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
}
}
String[] names = NAME_SEPARATOR.split(name);
if (ArrayUtils.isNotEmpty(names)) {
cacheActivateClass(clazz, names[0]); // ④
for (String n : names) {
cacheName(clazz, n); // ⑤
saveInExtensionClass(extensionClasses, clazz, n); // ⑥
}
}
}
}
①: 判断当前加载的class是否被@Adaptive注解修饰,如果是,则缓存扩展接口的自适应扩展实现类。即为cachedAdaptiveClass属性赋值为当前class。
②: 判断当前class是否为包装类,判断标准: 是否具有使用扩展接口为唯一参数的有参构造函数。如果是包装类,则将当前class放入到cachedWrapperClasses集合,因为一个类可以有多个包装类。
③: 从这里进入真正扩展类的加载。首先调用class的getConstructor方法,验证扩展类是否有无参构造函数。然后判断name是否为空,如果name为空,那么就使用class的simpleName作为扩展名称。
④: 缓存自动激活实现类。
⑤: 将class和扩展实现类名称对应关系放入cachedNames缓存。
⑥: 将扩展实现类名称和class对应关系放入extensionClasses缓存,extensionClasses作为loadExtensionClasses方法的返回结果,最后会放入到cachedClasses中。
3.3.5 injectExtension(T instance)实现
在createExtension方法中,有两出调用了injectExtension方法:
在完成了扩展实现类的创建之后,会调用injectExtension方法为其注入依赖的扩展接口。
如果cachedWrapperClasses不为空,那么就会创建包装类实例,并将被包装类作为属性注入。
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
if (isSetter(method)) { // ①
if (method.getAnnotation(DisableInject.class) != null) { // ②
continue;
}
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) { // ③
continue;
}
try {
String property = getSetterProperty(method); // ④
Object object = objectFactory.getExtension(pt, property); // ⑤
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("Failed to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
这里会用到objectFactory,上面提到ObjectFactory的实现有AdaptiveExtensionFactory、SpiExtensionFactory和SpringExtensionFactory。这里就使用了工厂模式,通过objectFactory获取需要注入的bean。因为在实例化ExtensionFactory扩展类时,是获取的ExtensionFactory的自适应扩展类,也就是说objectFactory持有的是AdaptiveExtensionFactory实例的应用。如果忘记可以看ExtendLoader的构造函数。
①: 属性注入是通过setter方法注入的,所有要装配其它扩展接口,需要为扩展接口提供setter方法。
②: 如果方法被@DisableInject注解修饰,则不支持注入。
③: 获取方法参数,判断参数是否为简单类型(String、基本数据类型之类)。
④: 获取setter方法的参数名。
⑤: 通过objectFactory获取扩展接口的实例,也就是调用AdaptiveExtensionFactory的getExtension方法。上面分析ExtensionFactory扩展接口的时候说到,SpiExtensionFactory的genExtension方法最终返回的是扩展接口的自适应扩展类,所以如果扩展点没有配置自适应扩展类的话,会报错 No adaptive method exist on extension xxx
,无法完成注入。
3.3.6 getAdaptiveExtension() 实现
上面讲到SpiExtensionFactory加载的bean是扩展接口的自适应扩展类,而且ExtensionLoader的getAdaptiveExtension方法是public的,因此用户也可以通过调用该方法获取扩展接口的自适应扩展类。在调试这个方式之前,需要在扩展接口的其中一个扩展实现类加上@Adaptive注解,作为其自适应扩展类。
@Adaptive // 标注为次类为扩展接口的自适应扩展类
public class BusinessCar implements Car {
@Override
public void run() {
System.out.println("business car running steady");
}
}
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;
}
①: 首先从CacheAdaptiveInstance缓存中获取自适应扩展类实例。
②: createAdaptiveInstanceError,在创建自适应扩展类时如果没有配置@Adaptive注解时,会抛出异常,异常结果赋值给createAdaptiveInstanceError,这是方便在多线程环境下,如果前面已经有线程尝试创建失败了,本次操作就可以不用继续尝试了。
③: 进入真正实例化自适应扩展类的操作。
private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
该方法执行了三步操作: 1. 获取自适应扩展类class;2. 通过反射创建自适应扩展类实例;3. 为自适应扩展类注入其它扩展类。
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
getAdaptiveExtensionClass获取自适应扩展类class,因为在执行getExtension方法的时候,就已经加载了所有扩展类,所以此时缓存中已经存在对应的自适应扩展类。
因为@Adaptive可以用于方法上,所有在加载所有的扩展类时,cachedAdaptiveClass不会被赋值,这时就会通过createAdaptiveExtensionClass创建自适应扩展类。
private Class<?> createAdaptiveExtensionClass() {
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);
}
createAdaptiveExtensionClass会生成xxx$Adaptive
的适配器类,xxx为你的扩展接口名称。具体生成过程是通过Compiler实现,Compiler也是一个SPI接口,dubbo提供了基于Jdk和Javassist的扩展类实现。
AdaptiveCompiler作为Compiler的自适应实现类,具体实现过程中,会加载dubbo配置文件的设置,缺省配置为javassist。也就是说AdaptiveCompiler会获取到配置中的编译器,然后实例化对应的编译器实例,最后由对应的编译器实例完成代码的生成。
可以通过 dubbo.application.compiler=jdk
或者<dubbo:application compiler="jdk">
修改默认编译器。
@SPI("sport")
public interface Car {
void run();
@Adaptive(value = {"name"})
void cost(URL url);
}
cost方法被@Adaptive注解修饰,ExtensionLoader会生成对应的xxx$Adaptive类。
package top.ljming.dubboprovider.spi.java;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Car$Adaptive implements top.ljming.dubboprovider.spi.java.Car {
public void cost(org.apache.dubbo.common.URL arg0) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg0;
String extName = url.getParameter("name", "sport");
if(extName == null)
throw new IllegalStateException("Failed to get extension (top.ljming.dubboprovider.spi.java.Car) name from url (" + url.toString() + ") use keys([name])");
top.ljming.dubboprovider.spi.java.Car extension = (top.ljming.dubboprovider.spi.java.Car)ExtensionLoader.getExtensionLoader(top.ljming.dubboprovider.spi.java.Car.class).getExtension(extName);
extension.cost(url);
}
}
String extName = url.getParameter("name", "sport"); 这行代码表示,如果URL传过来的name参数为空,则会获取@SPI注解的value作为默认值。
3.3.7 getActivateExtension方法实现
getActivateExtension(URL url, String key, String group)方法可以获取所有自动激活扩展点。参数分别为URL、URL中的key和URL中指定的group信息。
public void testDubboActivate() {
ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
Map<String, String> keyPair = new HashMap<>();
keyPair.put("impl", "sport,business");
keyPair.put("sport", "sport");
URL url = new URL("dobbo", "localhost", 1111, keyPair);
List<Car> carList = extensionLoader.getActivateExtension(url, "impl", "default");
for (Car car : carList) {
car.cost(url);
}
}
示例代码中,url的格式为dobbo://localhost:1111?impl=sport,business&sport=sport
public List<T> getActivateExtension(URL url, String key, String group) {
String value = url.getParameter(key);
return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group);
}
首先获取url中对应的key的value,然后value通过逗号分割,获取value数组,在调用getActivateExtension(URL url, String[] values, String group)
方法实现Activate类的加载。
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> exts = new ArrayList<>();
List<String> names = values == null ? new ArrayList<>(0) : Arrays.asList(values);
if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) { // ①
getExtensionClasses(); // ②
for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) { // ③
String name = entry.getKey();
Object activate = entry.getValue();
String[] activateGroup, activateValue;
if (activate instanceof Activate) {
activateGroup = ((Activate) activate).group();
activateValue = ((Activate) activate).value();
} else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) {
activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group();
activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value();
} else {
continue;
}
if (isMatchGroup(group, activateGroup)
&& !names.contains(name)
&& !names.contains(REMOVE_VALUE_PREFIX + name)
&& isActive(activateValue, url)) {
activateExtensions.add(getExtension(name));
}
}
exts.sort(ActivateComparator.COMPARATOR);
}
List<T> usrs = new ArrayList<>();
for (int i = 0; i < names.size(); i++) { // ④
String name = names.get(i);
if (!name.startsWith(REMOVE_VALUE_PREFIX)
&& !names.contains(REMOVE_VALUE_PREFIX + name)) {
if (DEFAULT_KEY.equals(name)) {
if (!usrs.isEmpty()) {
exts.addAll(0, usrs);
usrs.clear();
}
} else {
T ext = getExtension(name);
usrs.add(ext);
}
}
}
if (!usrs.isEmpty()) {
exts.addAll(usrs);
}
return exts;
}
①: 如果传过来的value中包含-defalut
, 那么所有的默认@Activate都不会被激活。
②: 加载所有的扩展类
③: 循环每个被@Activate注解修饰的类,根据传入的URL匹配条件(isMatchGroup方法),只有符合激活条件的扩展类才会被加载。然后排序,根据@Activate中配置的order值参数排序。
④: 遍历所有用户自定义的扩展类名称,根据用户URL配置的顺序,调整扩展点激活顺序。
以上是关于dubbo学习-SPI机制的主要内容,如果未能解决你的问题,请参考以下文章