源码版本是 2.7.8
在以前的文章中,介绍过 Java SPI机制 ,想了解的可以进去了解下。今天我们要讲的是 Dubbo SPI机制。
Dubbo SPI 实例 本实例参考的是dubbo官方给的官方实例。
首先定义一个接口,名称Robot。
1 2 3 public interface Robot { void sayHello () ; }
接下来定义两个实现类,分别是 OptimusPrime 和 Bumblebee。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class OptimusPrime implements Robot { @Override public void sayHello () { System.out.println("Hello, I am Optimus Prime." ); } } public class Bumblebee implements Robot { @Override public void sayHello () { System.out.println("Hello, I am Bumblebee." ); } }
接下来在META-INF/dubbo
文件夹下创建一个文件,名称为Robot 的全限定名 com.dubbo.provider.demo.Robot
(根据项目实际的全限定名),文件配置内容:
1 2 optimusPrime = org.apache.spi.OptimusPrime bumblebee = org.apache.spi.Bumblebee
接下来进入测试,在测试 Dubbo SPI时,需要在Robot接口上标注@SPI注解。下面是测试代码
1 2 3 4 5 6 7 8 9 10 11 12 public class DubboSpiTest { @Test public void sayHello () throws Exception { ExtensionLoader<Robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class); Robot optimusPrime = extensionLoader.getExtension("optimusPrime" ); optimusPrime.sayHello(); Robot bumblebee = extensionLoader.getExtension("bumblebee" ); bumblebee.sayHello(); } }
测试结果:
Dubbo SPI 源码分析 通过上面的简单实例演示,Dubbo 是通过 ExtensionLoader 的getExtensionLoader方法获取一个ExtensionLoader实例,然后再通过ExtensionLoader的getExtension方法获取扩展类对象。
ExtensionLoader 属性 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 private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*" );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 ExtensionFactory objectFactory;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 String cachedDefaultName;private volatile Throwable createAdaptiveInstanceError;private Set<Class<?>> cachedWrapperClasses;private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<>();private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
getExtensionLoader 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static <T> ExtensionLoader<T> getExtensionLoader (Class<T> type) { if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!" ); } 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; }
ExtensionLoader.getExtensionLoader
用于从缓存中获取与扩展类对应的ExtensionLoader,若缓存没命中,则创建一个新的实例。
getExtension 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 public T getExtension (String name) { return getExtension(name, true ); } 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; } public T getDefaultExtension () { getExtensionClasses(); if (StringUtils.isBlank(cachedDefaultName) || "true" .equals(cachedDefaultName)) { return null ; } return getExtension(cachedDefaultName); }
上面代码逻辑比较清晰,首先检测缓存,如果缓存未命中则创建扩展类对象。
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 38 39 40 41 42 43 44 45 private T createExtension (String name, boolean wrap) { 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); 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; } catch (Throwable t) { throw new IllegalStateException("Extension instance (name: " + name + ", class: " + type + ") couldn't be instantiated: " + t.getMessage(), t); } }
createExtension 的逻辑步骤如下:
通过 getExtensionClasses 获取所有扩展类
通过反射创建扩展对象
Wrapper 对象中
将扩展对象包裹在相应的 Wrapper 对象中
后面两步,是dubbo ioc 和 aop 的具体实现。
getExtensionClasses 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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 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 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()); loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache" , "com.alibaba" ), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages()); } return extensionClasses; } private void cacheDefaultExtensionName () { final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation == null ) { return ; } String value = defaultAnnotation.value(); if ((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 ]; } } }
loadDirectory 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 private void loadDirectory (Map<String, Class<?>> extensionClasses, String dir, String type, boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) { String fileName = dir + type; Enumeration<java.net.URL> urls = null ; ClassLoader classLoader = findClassLoader(); if (extensionLoaderClassLoaderFirst) { ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader(); if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) { urls = extensionLoaderClassLoader.getResources(fileName); } } if (urls == null || !urls.hasMoreElements()) { if (classLoader != null ) { urls = classLoader.getResources(fileName); } else { urls = ClassLoader.getSystemResources(fileName); } } if (urls != null ) { while (urls.hasMoreElements()) { java.net.URL resourceURL = urls.nextElement(); loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages); } } }
loadResource 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 private void loadResource (Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL, boolean overridden, String... excludedPackages) { try { try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null ) { final int ci = line.indexOf('#' ); if (ci >= 0 ) { line = line.substring(0 , ci); } line = line.trim(); if (line.length() > 0 ) { try { String name = null ; int i = line.indexOf('=' ); if (i > 0 ) { name = line.substring(0 , i).trim(); line = line.substring(i + 1 ).trim(); } if (line.length() > 0 && !isExcluded(line, excludedPackages)) { loadClass(extensionClasses, resourceURL, Class.forName(line, true , classLoader), name, overridden); } } catch (Throwable t) { IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t); exceptions.put(line, e); } } } } } catch (Throwable t) { logger.error("Exception occurred when loading extension class (interface: " + type + ", class file: " + resourceURL + ") in " + resourceURL, t); } }
loadResource 方法主要用于读取和解析配置文件,并通过反射架子啊类,最后调用 loadClass 方法进行其他操作。
loadClass 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 void loadClass (Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name, boolean overridden) throws NoSuchMethodException { if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Error occurred when loading extension class (interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + " is not subtype of interface." ); } if (clazz.isAnnotationPresent(Adaptive.class)) { cacheAdaptiveClass(clazz, overridden); } 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, overridden); } } } }
loadClass 方法操作了不同的缓存,比如 cachedAdaptiveClass、cachedWrapperClasses 和 cachedNames 等等
Reference