這篇文章主要講解了“Dubbo的SPI機制介紹以及SPI加載class的方法”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Dubbo的SPI機制介紹以及SPI加載class的方法”吧!
成都創(chuàng)新互聯(lián)公司是專業(yè)的祿勸網(wǎng)站建設(shè)公司,祿勸接單;提供網(wǎng)站設(shè)計制作、成都網(wǎng)站制作,網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進行祿勸網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!
@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!"); } // 從緩存中獲取與拓展類對應(yīng)的ExtensionLoader ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); if (loader == null) { // 若緩存未命中,則創(chuàng)建一個新的實例,先簡單看下new ExtensionLoader<T>(type) EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); } return loader; }
這里的objectFactory創(chuàng)建可以參考: Dubbo的SPI機制分析3-Dubbo的IOC依賴注入
private ExtensionLoader(Class<?> type) { this.type = type; // 這里的type是Robot.class,所以會執(zhí)行后面的代碼,加載并創(chuàng)建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) { // 創(chuàng)建拓展實例,下面分析createExtension(name) instance = createExtension(name); holder.set(instance); } } } return (T) instance; }
這里依賴注入可以參考: Dubbo的SPI機制分析3-Dubbo的IOC依賴注入
private T createExtension(String name) { // 從配置文件中加載所有的拓展類,可得到“配置項名稱”到“配置類”的映射關(guān)系表 // 加載完后根據(jù)name獲取,得到形如com.alibaba.dubbo.demo.provider.spi.OptimusPrime // 待會重點分析getExtensionClasses(),刪去了一些非關(guān)鍵代碼 Class<?> clazz = getExtensionClasses().get(name); try { // 也是嘗試先從緩存獲取,獲取不到通過反射創(chuàng)建一個并放到緩存中 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() { // 從緩存中獲取映射關(guān)系表 Map<string, class<?>> classes = cachedClasses.get(); // 雙重檢查 if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { // 若緩存中沒有,去加載映射關(guān)系 classes = loadExtensionClasses(); cachedClasses.set(classes); } } } return classes; }
private Map<string, class<?>> loadExtensionClasses() { // 獲取SPI注解,這里的type是在調(diào)用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注解中的值,并緩存起來,可以關(guān)注下,后面會用到 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(); // 根據(jù)文件名加載所有同名文件 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; // 按行讀取配置內(nèi)容 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); } } } } } } }
標(biāo)注1,這里的可以參考: Dubbo的SPI機制分析2-Adaptive詳解
標(biāo)注2,這里的可以參考: Dubbo的SPI機制分析4-Dubbo通過Wrapper實現(xiàn)AOP
標(biāo)注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是否為標(biāo)注了@Adaptive注解,標(biāo)注1 if (clazz.isAnnotationPresent(Adaptive.class)) { if (cachedAdaptiveClass == null) { cachedAdaptiveClass = clazz; } else if (!cachedAdaptiveClass.equals(clazz)) { // 拋異常,不能有多個標(biāo)注有@Adaptive注解的類 } } // 判斷是否是Wrapper類型,標(biāo)注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是否有默認(rèn)的構(gòu)造方法,如果沒有,則拋出異常 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根據(jù)條件激活,標(biāo)注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的映射關(guān)系,這樣就解析好了一行 extensionClasses.put(n, clazz); } else if (c != clazz) { // 拋異常 } } } } }
感謝各位的閱讀,以上就是“Dubbo的SPI機制介紹以及SPI加載class的方法”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Dubbo的SPI機制介紹以及SPI加載class的方法這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
文章名稱:Dubbo的SPI機制介紹以及SPI加載class的方法
轉(zhuǎn)載來于:http://www.rwnh.cn/article34/gshose.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App開發(fā)、域名注冊、搜索引擎優(yōu)化、網(wǎng)站設(shè)計、標(biāo)簽優(yōu)化、網(wǎng)站排名
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)