您好,登錄后才能下訂單哦!
這篇文章主要講解了“Dubbo的SPI機制介紹以及SPI加載class的方法”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Dubbo的SPI機制介紹以及SPI加載class的方法”吧!
@SPI public interface Robot { void sayHello(); } 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."); } }
public class DubboSPITest { @Test public void sayHelloDubbo() throws Exception { ExtensionLoader<robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class); Robot optimusPrime = extensionLoader.getExtension("optimusPrime"); optimusPrime.sayHello(); Robot bumblebee = extensionLoader.getExtension("bumblebee"); bumblebee.sayHello(); } }
輸出: Hello, I am Optimus Prime. Hello, I am Bumblebee.
ExtensionLoader<robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class); 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 interface!"); } if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!"); } // 從緩存中獲取與拓展類對應的ExtensionLoader ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); if (loader == null) { // 若緩存未命中,則創建一個新的實例,先簡單看下new ExtensionLoader<T>(type) EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); } return loader; }
這里的objectFactory創建可以參考: Dubbo的SPI機制分析3-Dubbo的IOC依賴注入
private ExtensionLoader(Class<?> type) { this.type = type; // 這里的type是Robot.class,所以會執行后面的代碼,加載并創建SpiExtensionFactory和SpringExtensionFactory, objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
public T getExtension(String name) { if ("true".equals(name)) { return getDefaultExtension(); } // Holder也是用于持有對象的,用作緩存 Holder<object> holder = cachedInstances.get(name); if (holder == null) { cachedInstances.putIfAbsent(name, new Holder<object>()); holder = cachedInstances.get(name); } Object instance = holder.get(); if (instance == null) { synchronized (holder) { instance = holder.get(); if (instance == null) { // 創建拓展實例,下面分析createExtension(name) instance = createExtension(name); holder.set(instance); } } } return (T) instance; }
這里依賴注入可以參考: Dubbo的SPI機制分析3-Dubbo的IOC依賴注入
private T createExtension(String name) { // 從配置文件中加載所有的拓展類,可得到“配置項名稱”到“配置類”的映射關系表 // 加載完后根據name獲取,得到形如com.alibaba.dubbo.demo.provider.spi.OptimusPrime // 待會重點分析getExtensionClasses(),刪去了一些非關鍵代碼 Class<?> clazz = getExtensionClasses().get(name); try { // 也是嘗試先從緩存獲取,獲取不到通過反射創建一個并放到緩存中 T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } // 依賴注入和cachedWrapperClasses,后面分析 injectExtension(instance); Set<Class<?>> wrapperClasses = cachedWrapperClasses; if (wrapperClasses != null && !wrapperClasses.isEmpty()) { for (Class<?> wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } return instance; } }
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; }
private Map<string, class<?>> loadExtensionClasses() { // 獲取SPI注解,這里的type是在調用getExtensionLoader方法時傳入的 final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation != null) { String value = defaultAnnotation.value(); if ((value = value.trim()).length() > 0) { String[] names = NAME_SEPARATOR.split(value); if (names.length > 1) { // 拋異常 } // 獲取@SPI注解中的值,并緩存起來,可以關注下,后面會用到 if (names.length == 1) cachedDefaultName = names[0]; } } Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>(); // 加載指定文件夾下的配置文件,META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/ loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY); // 我是放在這個目錄下的 loadDirectory(extensionClasses, DUBBO_DIRECTORY); loadDirectory(extensionClasses, SERVICES_DIRECTORY); return extensionClasses; }
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) { // fileName是META-INF/dubbo/com.alibaba.dubbo.demo.provider.spi.Robot String fileName = dir + type.getName(); 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()) { // 一個resourceURL就是一個文件 java.net.URL resourceURL = urls.nextElement(); loadResource(extensionClasses, classLoader, resourceURL); } } } }
private void loadResource(Map<string, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) { try { BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8")); try { 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) { // 真正的去加載類 loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name); } } } } } } }
標注1,這里的可以參考: Dubbo的SPI機制分析2-Adaptive詳解
標注2,這里的可以參考: Dubbo的SPI機制分析4-Dubbo通過Wrapper實現AOP
標注3,這里的cachedActivates可以參考: Dubbo的SPI機制分析5-Activate詳解
private void loadClass(Map<string, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { // clazz必須是type類型的,否則拋異常 if (!type.isAssignableFrom(clazz)) { } // 判斷clazz是否為標注了@Adaptive注解,標注1 if (clazz.isAnnotationPresent(Adaptive.class)) { if (cachedAdaptiveClass == null) { cachedAdaptiveClass = clazz; } else if (!cachedAdaptiveClass.equals(clazz)) { // 拋異常,不能有多個標注有@Adaptive注解的類 } } // 判斷是否是Wrapper類型,標注2 else if (isWrapperClass(clazz)) { Set<Class<?>> wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet<Class<?>>(); wrappers = cachedWrapperClasses; } wrappers.add(clazz); } // 程序進入此分支,表明clazz是一個普通的拓展類,Robot就是一個普通的拓展類 else { // 檢測clazz是否有默認的構造方法,如果沒有,則拋出異常 clazz.getConstructor(); if (name == null || name.length() == 0) { name = findAnnotationName(clazz); if (name.length() == 0) { // 拋異常 } } String[] names = NAME_SEPARATOR.split(name); if (names != null && names.length > 0) { // 用于@Activate根據條件激活,標注3 Activate activate = clazz.getAnnotation(Activate.class); if (activate != null) { cachedActivates.put(names[0], activate); } for (String n : names) { if (!cachedNames.containsKey(clazz)) { cachedNames.put(clazz, n); } Class<?> c = extensionClasses.get(n); if (c == null) { // 存儲名稱到class的映射關系,這樣就解析好了一行 extensionClasses.put(n, clazz); } else if (c != clazz) { // 拋異常 } } } } }
感謝各位的閱讀,以上就是“Dubbo的SPI機制介紹以及SPI加載class的方法”的內容了,經過本文的學習后,相信大家對Dubbo的SPI機制介紹以及SPI加載class的方法這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。