前面提到了java的SPI機制,如今咱們看看dubbo的SPI機制。
HelloSpiService接口,要加上@SPI
註解java
@SPI public interface HelloSpiService { void sayHello(); }
實現類HelloSpiServiceImpl同java的SPI機制。
在資源META-INF/dubbo
文件下建立一個文件com.learn.dubbo.spi.HelloSpiService,文件內容不一樣與java的SPI的是用鍵值對。這樣的好處是隻加載須要的擴展點,節約資源。apache
helloSpiService=com.learn.dubbo.spi.impl.HelloSpiServiceImpl
測試代碼:segmentfault
public class DubboTest { public static void main(String[] args) { ExtensionLoader<HelloSpiService> extensionLoader = ExtensionLoader.getExtensionLoader(HelloSpiService.class); HelloSpiService helloSpiService = extensionLoader.getExtension("helloSpiService"); helloSpiService.sayHello(); } }
運行結果以下:緩存
主要作的事情:app
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { if (type == null) { throw new IllegalArgumentException("Extension type == null"); } if (!type.isInterface()) { throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!"); } 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; } private ExtensionLoader(Class<?> type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
獲取擴展實例,沒有則建立函數
public T getExtension(String name) { if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("Extension name == null"); } if ("true".equals(name)) { // 獲取默認的擴展點 return getDefaultExtension(); } // 獲取或建立Holder對象 final Holder<Object> holder = getOrCreateHolder(name); // holder對象沒有值,則建立拓展實例 Object instance = holder.get(); if (instance == null) { synchronized (holder) { instance = holder.get(); if (instance == null) { instance = createExtension(name); holder.set(instance); } } } // 返回拓展實例 return (T) instance; } private Holder<Object> getOrCreateHolder(String name) { // 緩存沒有,則建立完放入緩存 Holder<Object> holder = cachedInstances.get(name); if (holder == null) { // putIfAbsent防止一個name對應多個Holder cachedInstances.putIfAbsent(name, new Holder<>()); holder = cachedInstances.get(name); } return holder; }
從配置信息獲取類信息,判斷是否實例化過,沒有則實例化對象放入緩存源碼分析
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); } }
先從緩存獲取,緩存獲取不到,再調用loadExtensionClasses從配置文件獲取信息實例化測試
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; }
驗證SPI註解內容,再從文件夾讀取配置文件,咱們配置的META-INF/dubbo/
就是這裏讀取的。this
private Map<String, Class<?>> loadExtensionClasses() { // 驗證SPI註解內容 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; }
經過文件路徑獲取資源路徑url
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) { // 獲取文件路徑 String fileName = dir + type; try { Enumeration<java.net.URL> urls; // 獲取類加載器 ClassLoader classLoader = findClassLoader(); // 獲取資源路徑 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); } } } catch (Throwable t) { logger.error("Exception occurred when loading extension class (interface: " + type + ", description file: " + fileName + ").", t); } }
讀取文件內容,解析後,實例化對象並緩存。
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) { 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 { // 獲取key和value 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) { loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name); } } 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); } }
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) 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."); } // Adaptive註解 if (clazz.isAnnotationPresent(Adaptive.class)) { cacheAdaptiveClass(clazz); // 是否Wrapper類型 } 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); } } } }