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.SportCartop.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 fastbusiness 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.SportCarbusiness=top.ljming.dubboprovider.spi.java.BusinessCar
// 改造Car接口@SPI("sport") // 添加SPI注解,表示Car的默认实现为sportpublic interface Car { void run();}
// 测试方法@Testpublic 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 扩展点的特征

扩展类一共包含四种特性:自动包装、自动加载、自适应和自动激活。

  1. 自动包装

    扩展类在被加载时,如果发现这个扩展类包含其它扩展点作为构造函数的参数,则这个扩展类就会被认为是Wrapper类。

  2. 自动加载

    除了在构造函数中传入其它扩展实例,我们还经常使用setter方法设置属性值。如果某个扩展类是另外一个扩展点类的成员属性,并且拥有setter方法,那么框架也会自动注入对应的扩展点实例。

  3. 自适应

    在dubbo spi中,使用@Adaptive注解,可以动态的通过url中的参数来确定要使用那个具体的实现类。从而解决自动加载中的实例注入问题。

  4. 自动激活

    使用@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方法,用于返回扩展类实例。

@SPIpublic interface ExtensionFactory { <T> T getExtension(Class<T> type, String name);}

ExtensionFactory有三个扩展实现类,分别是AdaptiveExtensionFactory、SpiExtensionFactory、SpringExtensionFactory。AdaptiveExtensionFactory扩展实现类被@Adaptive修饰,作为ExtensionFactory的自适应实现。SpiExtensionFactory是基于dubbo的spi实现。SpringExtensionFactory基于spring容器,根据名称从spring容器中获取对应的实例。

AdaptiveExtensionFactory

@Adaptivepublic 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机制的主要内容,如果未能解决你的问题,请参考以下文章

理解 Dubbo SPI 扩展机制

深入理解 Dubbo SPI 扩展机制

dubbo spi扩展实现机制javasist

学习手写Dubbo中的SPI

Dubbo的SPI机制——AOP

来撸一撸Dubbo之SPI机制源码,SPI到底解决了什么问题?