SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。SPI 机制在第三方框架中也有所应用,比如 Dubbo 就是通过 SPI 机制加载所有的组件。不过,Dubbo 并未使用 java 原生的 SPI 机制,而是对其进行了增强,使其能够更好的满足需求。在 Dubbo 中,SPI 是一个非常重要的模块。基于 SPI,我们可以很容易的对 Dubbo 进行拓展。
Dubbo SPI介绍 Dubbo SPI实现了自己的IOC和AOP机制,其中服务的实例被称为Extension(扩展),类似于Spring框架中bean的角色,是一个扩展实现类的实例。Dubbo框架中所有类的实例基本都是使用了SPI的方式获取。所以了解SPI的实现机制是学习Dubbo的重中之重。
Dubbo SPI示例 这里以apache dubbo 2.x版本为例,引入dubbo依赖
1 2 3 4 5 <dependency > <groupId > org.apache.dubbo</groupId > <artifactId > dubbo</artifactId > <version > 2.7.15</version > </dependency >
编写扩展类的接口 @SPI的value为默认实现,@Adpative则为自适应加载注解,在代码运行时根据传入的URL(必传)参数动态加载,这个后面会详细讲解
注:URL.class为Dubbo自定义,全限名:org.apache.dubbo.common.URL
1 2 3 4 5 6 7 8 @SPI("apple") public interface Fruit { void printName () ; @Adaptive("name") void printName (URL url) ; }
编写Fruit接口的实现类:Apple、Banana、Orange
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Apple implements Fruit { private static final String _name = "Apple" ; @Override public void printName () { System.out.println(_name); } @Override public void printName (URL url) { this .printName(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Banana implements Fruit { private static final String _name = "Banana" ; @Override public void printName () { System.out.println(_name); } @Override public void printName (URL url) { this .printName(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Orange implements Fruit { private static final String _name = "Orange" ; @Override public void printName () { System.out.println(_name); } @Override public void printName (URL url) { this .printName(); } }
在resources添加目录:META-INF/dubbo
新建文件(dubbo约定文件名必须与接口全限定名一致):com.xx.xx.Fruit
编辑文件内容并保存,建议以kv形式保存,如果像Orange的配置,则Dubbo默认会以类名“orange”作为实现类的名称:
1 2 3 4 apple =com.xx.xx.Apple banana =com.xx.xx.Banana com.xx.xx.Orange
编写Main方法测试运行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static void main (String[] args) { ExtensionLoader<Fruit> FruitExtensionLoader = ExtensionLoader.getExtensionLoader(Fruit.class); Fruit fruit1 = FruitExtensionLoader.getDefaultExtension(); fruit1.printName(); System.out.println("==============================" ); Fruit fruit2 = FruitExtensionLoader.getExtension("banana" ); fruit2.printName(); System.out.println("==============================" ); Map<String, String> parameters = new HashMap <>(2 ); parameters.put("name" , "orange" ); URL url = new URL ("" , "" , 0 , parameters); Fruit fruit3 = FruitExtensionLoader.getAdaptiveExtension(); fruit3.printName(url); System.out.println("==============================" ); }
执行结果:
1 2 3 4 5 6 Apple ============================== Banana ============================== Orange ==============================
源码解析
SPI核心入口ExtensionLoader.getExtensionLoader()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap <>();****** public static <T> ExtensionLoader<T> getExtensionLoader (Class<T> type) { ****** 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; }
EXTENSION_LOADERS为ExtensionLoader实例的缓存缓存
看ExtensionLoader构造函数,ExtensionFactory可以近似理解为Spring中的BeanFactory,作为获取Extension实例的工厂,且支持自适应扩展
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private final ExtensionFactory objectFactory;****** private ExtensionLoader (Class<?> type) { this .type = type; if (type == ExtensionFactory.class) { objectFactory = null ; } else { objectFactory = ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension(); } }
查看接口ExtensionFactory的实现类,这里先讲解Dubbo自己的SpiExtensionFactory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 ; } }
查看loader.getSupportedExtensions()
1 2 3 4 public Set<String> getSupportedExtensions () { Map<String, Class<?>> clazzes = getExtensionClasses(); return Collections.unmodifiableSet(new TreeSet <>(clazzes.keySet())); }
进入getExtensionClasses(),这里为一个double check lock实现的单例模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private final Holder<Map<String, Class<?>>> cachedClasses = new Holder <>();****** 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; }
进入loadExtensionClasses(),开始装载扩展类
1 2 3 4 5 6 7 8 9 10 11 12 private Map<String, Class<?>> loadExtensionClasses() { cacheDefaultExtensionName(); Map<String, Class<?>> extensionClasses = new HashMap <>(); for (LoadingStrategy strategy : strategies) { loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages()); } return extensionClasses; }
strategies使用jdk自带的ServiceLoader装载多种dubbo spi装载方式,本文主要看DubboLoadingStrategy,其中声明了配置文件读取路径为”META-INF/dubbo/“
1 2 3 4 5 6 7 8 public class DubboLoadingStrategy implements LoadingStrategy { @Override public String directory () { return "META-INF/dubbo/" ; } }
进入loadDirectory(),这里会根据LoadingStrategy配置的文件目录,解析并读取
核心执行顺序为:loadDirectory() > loadResource() > loadClass()
加载文件目录 > 加载spi配置文件 > 加载配置文件中的扩展实现类
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 private void loadDirectory (Map<String, Class<?>> extensionClasses, String dir, String type, boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) { String fileName = dir + type; ****** loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages); ****** } private void loadResource (Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL, boolean overridden, String... excludedPackages) { ****** loadClass(extensionClasses, resourceURL, Class.forName(clazz, true , classLoader), name, overridden); ****** } private void loadClass (Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name, boolean overridden) throws NoSuchMethodException { if (clazz.isAnnotationPresent(Adaptive.class)) { cacheAdaptiveClass(clazz, overridden); } else if (isWrapperClass(clazz)) { cacheWrapperClass(clazz); } else { clazz.getConstructor(); if (StringUtils.isEmpty(name)) { name = findAnnotationName(clazz); } 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, overridden); } } } }
到这,Dubbo SPI已经完成了对扩展接口实现类(注意是class,此时类还并未实例化)的扫描和缓存;
接下来回头看ExtensionLoader#getDefaultExtension
1 2 3 4 5 6 7 8 9 public T getDefaultExtension () { getExtensionClasses(); if (StringUtils.isBlank(cachedDefaultName) || "true" .equals(cachedDefaultName)) { return null ; } return getExtension(cachedDefaultName); }
进入getExtension,开始实例化扩展实现类,这里同样使用了double check lock保证单例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public T getExtension (String name, boolean wrap) { 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, wrap); holder.set(instance); } } } return (T) instance; }
进入createExtension,开始创建实例并出初始化实例
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 private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap <>(64 );****** private T createExtension (String name, boolean wrap) { Class<?> clazz = getExtensionClasses().get(name); T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null ) { EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } injectExtension(instance); if (wrap) { List<Class<?>> wrapperClassesList = new ArrayList <>(); if (cachedWrapperClasses != null ) { wrapperClassesList.addAll(cachedWrapperClasses); wrapperClassesList.sort(WrapperComparator.COMPARATOR); Collections.reverse(wrapperClassesList); } if (CollectionUtils.isNotEmpty(wrapperClassesList)) { for (Class<?> wrapperClass : wrapperClassesList) { Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class); if (wrapper == null || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } } } initExtension(instance); return instance; }
最后获取到扩展接口的默认实现类实例
ExtensionLoader#getExtension()也是同理,这里将扩展实现类的名称已参数的形式传入,而不是读取@SPI的声明,这里不再赘述
接下来看getAdaptiveExtension方法,根据参数获取自适应实现类,这里仍然使用了单例模式
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 T getAdaptiveExtension() { Object instance = cachedAdaptiveInstance.get(); if (instance == null) { if (createAdaptiveInstanceError != null) { throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); } 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); } } } } return (T) instance; }
进入createAdaptiveExtension
1 2 3 4 5 6 7 8 9 10 11 12 13 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); } }
进入getAdaptiveExtensionClass
1 2 3 4 5 6 7 8 private Class<?> getAdaptiveExtensionClass() { getExtensionClasses(); if (cachedAdaptiveClass != null ) { return cachedAdaptiveClass; } return cachedAdaptiveClass = createAdaptiveExtensionClass(); }
进入createAdaptiveExtensionClass,这里Dubbo为被@Adaptive声明的方法生成自适应实现,即自适应代理类
1 2 3 4 5 6 7 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); }
以上文Demo中的Fruit#printName(URL url)为例,Dubbo生成的代理类源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Fruit$Adaptive implements com .xx.xx.Fruit { public void printName () { throw new UnsupportedOperationException ("The method public abstract void com.xx.xx.Fruit.printName() of interface com.xx.xxFruit is not adaptive method!" ); } public void printName (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" , "apple" ); if (extName == null ) throw new IllegalStateException ("Failed to get extension (com.xx.xx.Fruit) name from url (" + url.toString() + ") use keys([name])" ); com.xx.xx.Fruit extension = (com.xx.xx.Fruit) ExtensionLoader.getExtensionLoader(com.xx.xx.Fruit.class).getExtension(extName); extension.printName(arg0); } }
到这里我们可以看到Dubbo的自适应扩展,其实现方式还是使用了ExtensionLoader.getExtensionLoader(Class).getExtension(name);
参数name则是从URL中获取,key在@Adaptive注解中声明,value则在代码运行时动态输入,Dubbo则依靠这个机制实现动态自适应扩展类加载。
本文只是简单介绍了Dubbo SPI的简单使用和主要实现原理,源码中还有诸多细节,无法全面覆盖,待后续补充 ……