1 public interface Robot { 2 void sayHello(); 3 } 4 5 public class OptimusPrime implements Robot { 6 7 @Override 8 public void sayHello() { 9 System.out.println("Hello, I am Optimus Prime."); 10 } 11 } 12 13 public class Bumblebee implements Robot { 14 15 @Override 16 public void sayHello() { 17 System.out.println("Hello, I am Bumblebee."); 18 } 19 }
接下來,在項目(以一個典型的maven項目爲例)的「resources/META-INF/services」路徑下建立一個文件,名稱爲Robot的全限定名「org.apache.spi.Robot」。文件內容以下:java
org.apache.spi.OptimusPrime org.apache.spi.Bumblebee
編寫測試代碼,運行以後能夠看到兩個實現類被加載並調用了sayHello方法(調用結果演示略)。spring
public class JavaSPITest { @Test public void sayHello() throws Exception { ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class); System.out.println("Java SPI"); serviceLoader.forEach(Robot::sayHello); } }
//使用SPI註解標註的接口 @SPI public interface Robot { void sayHello(); }
接下來,在項目(以一個典型的maven項目爲例)的「resources/META-INF/dubbo」路徑下建立一個文件,名稱爲Robot的全限定名「org.apache.spi.Robot」。文件內容以下:apache
optimusPrime=org.apache.spi.OptimusPrime bumblebee=org.apache.spi.Bumblebee
//測試類 public class DubboSPITest { @Test public void sayHello() throws Exception { //傳入一個標註有@SPI的接口Class,經過getExtensionLoader獲取該SPI接口的ExtensionLoader實例 ExtensionLoader<Robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class); //傳入要獲取的實現類的name(META-INF/dubbo/org.apache.spi.Robot文件下的name),獲取實現類實例 Robot optimusPrime = extensionLoader.getExtension("optimusPrime"); optimusPrime.sayHello(); Robot bumblebee = extensionLoader.getExtension("bumblebee"); bumblebee.sayHello(); } }
經過與JDK SPI示例的比較,發現Dubbo SPI與JDK SPI最大的不一樣就是Dubbo SPI經過鍵值對的方式進行配置。這樣最大的好處是能夠按需加載指定的實現類(經過name指定)。下面就讓咱們以本例中getExtensionLoader與getExtension兩個方法做爲引子,分析Dubbo SPI的實現源碼。數組
/**************************************** 相關字段 ****************************************/ //ExtensionLoader對應的SPI接口類型 private final Class<?> type; //ExtensionLoader對應的ExtensionFactory實例 private final ExtensionFactory objectFactory; //ExtensionLoader全局緩存,key爲SPI接口類型,value爲相應的ExtensionLoader實例 private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(); /**************************************** 相關方法 ****************************************/ /** * 靜態方法,根據SPI接口類型獲取相應的ExtensionLoader */ public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { //判斷type是否爲空、是不是接口類型、是否具備@SPI註解 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..."); } //從ExtensionLoader的緩存中根據SPI接口類型獲取對應的ExtensionLoader實例 ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); //若緩存沒有該實例,則new一個,而且存放入緩存,key爲type if (loader == null) { EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); } return loader; } /** * 私有構造器,對調用者而言,只能經過getExtensionLoader方法獲取ExtensionLoader實例 */ private ExtensionLoader(Class<?> type) { //保存該ExtensionLoader的SPI接口類型信息 this.type = type; //若SPI接口類型爲ExtensionFactory,則不設置字段ExtensionFactory, //不然須要設置一個ExtensionFactory。具體的獲取邏輯咱們後面會講到 objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
/**************************************** 實例緩存相關字段 ****************************************/ //所有SPI接口的擴展類實例緩存,key爲擴展類Class(每個SPI接口的每個實現類的Class都不一樣) //value爲對應的擴展類實例。注意該緩存要與另一個相似的緩存cachedInstances區分開。 private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(); //每一個ExtensionLoader對應的SPI接口的擴展類實例緩存, //key爲擴展類的name,value爲Holder對象,其持有/維護擴展類的實例。 private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>(); /**************************************** Class緩存相關字段 ****************************************/ //每一個ExtensionLoader對應的SPI接口的擴展類Class緩存,key爲擴展類的name,value爲擴展類Class private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>(); //SPI接口的擴展類中具備@Adaptive註解的擴展類Class緩存 private volatile Class<?> cachedAdaptiveClass = null; //SPI接口的擴展類中被斷定爲具備包裝功能的擴展類Class緩存 private Set<Class<?>> cachedWrapperClasses; //SPI接口的擴展類中具備@Activate註解的緩存,key是擴展類names[0],value爲@Activate的Class private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>(); /**************************************** name緩存相關字段 ****************************************/ //SPI接口的擴展類的name緩存,key爲擴展類的Class,value爲擴展類的names[0] private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>(); //SPI接口的默認擴展類的name private String cachedDefaultName; /**************************************** 其餘相關字段 ****************************************/ //ExtensionLoader對應的SPI接口Class private final Class<?> type; /**************************************** 相關方法 ****************************************/ /** * 獲取ExtensionLoader對應的SPI接口的擴展類實例 */ public T getExtension(String name) { //檢查擴展類的name if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("Extension name == null"); } //若是傳入的name值爲true,則獲取默認的SPI接口擴展類實例 if ("true".equals(name)) { return getDefaultExtension(); } //getOrCreateHolder方法比較簡單,它從緩存「cachedInstances」中獲取該name對應的Holder實例, //Holder是一個「持有類」,其可能持有擴展類實例 Holder<Object> holder = getOrCreateHolder(name); Object instance = holder.get(); //若是未獲取到實例,在監視器鎖中進行第二次獲取與建立 if (instance == null) { synchronized (holder) { instance = holder.get(); if (instance == null) { //建立name對應的擴展類實例,並緩存到cachedInstances中 instance = createExtension(name); holder.set(instance); } } } return (T) instance; } /** * 獲取默認的擴展類實例 */ public T getDefaultExtension() { //調用getExtensionClasses方法獲取SPI接口配置的擴展類信息,返回結果爲一個Map<String, Class>, //其中key爲擴展類name,value爲該name對應的擴展類Class。 getExtensionClasses(); //若是SPI接口默認擴展類name爲空或者爲true,則默認的擴展類實例爲null if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) { return null; } //不然調用getExtension根據默認的擴展類name去獲取實例 return getExtension(cachedDefaultName); } /** * 建立一個擴展類的實例 */ private T createExtension(String name) { //調用getExtensionClasses方法,從返回結果中獲取name對應的擴展類Class,若是Class爲null,則拋出異常 Class<?> clazz = getExtensionClasses().get(name); if (clazz == null) { throw findException(name); } try { //從緩存「EXTENSION_INSTANCES」中根據擴展類Class獲取實例 T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { //緩存未命中則經過反射建立一個實例,並存入緩存 EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } //向這個擴展類實例注入其所須要的屬性(以setXXX爲準),該方法比較複雜,咱們後面會進行分析 injectExtension(instance); //獲取SPI接口擴展類中具備包裝功能的擴展類Class緩存,而後對instance進行層層包裝(裝飾器模式), //對每次包裝出來的新實例進行屬性注入,所有包裝完成後讓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 ... couldn't be instantiated: "); } } /** * 獲取SPI接口配置的擴展類信息 */ private Map<String, Class<?>> getExtensionClasses() { //從緩存「cachedClasses」中獲取已加載的擴展類 Map<String, Class<?>> classes = cachedClasses.get(); if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { //緩存爲空,調用loadExtensionClasses方法加載SPI接口配置的擴展類信息,並緩存結果 classes = loadExtensionClasses(); cachedClasses.set(classes); } } } return classes; } /** * 加載SPI接口配置的擴展類信息 */ private Map<String, Class<?>> loadExtensionClasses() { //獲取SPI接口默認擴展類的name並進行緩存 cacheDefaultExtensionName(); //讀取並解析SPI接口的配置文件,去幾個固定的目錄下讀取 //(1):META-INF/services/SPI接口全限定類名(或SPI接口全限定類名替換org.apache爲com.alibaba) //(2):META-INF/dubbo/SPI接口全限定類名(或SPI接口全限定類名替換org.apache爲com.alibaba) //(3):META-INF/dubbo/internal/SPI接口全限定類名(或SPI接口全限定類名替換org.apache爲com.alibaba) 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; } /** * 獲取SPI接口默認擴展類的name並進行緩存 */ private void cacheDefaultExtensionName() { //獲取ExtensionLoader對應的SPI接口上的SPI註解 final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation != null) { //獲取SPI註解的value值並進行校驗 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 ..."); } if (names.length == 1) { //將其做爲SPI接口默認擴展類的name並進行緩存 cachedDefaultName = names[0]; } } } } /** * 加載SPI接口的配置文件 */ 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) { //遍歷獲取到的URL,並調用loadResource去加載資源 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: ..."); } } /** * 在loadDirectory的基礎上,對每一個文件中的內容進行加載與解析 */ 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 { String name = null; //按「=」號截取name和擴展類的全限定類名,能夠看出name是能夠沒有的 int i = line.indexOf('='); if (i > 0) { name = line.substring(0, i).trim(); line = line.substring(i + 1).trim(); } //若是line,即擴展類全限定類名不爲空,經過Class.forName獲取其Class, //而後調用loadClass繼續加載該Class if (line.length() > 0) { loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name); } } catch (Throwable t) { IllegalStateException e = new IllegalStateException("Failed to load ..."); exceptions.put(line, e); } } } } } catch (Throwable t) { logger.error("Exception occurred when loading extension class (interface: ..."); } } /** * 在loadResource的基礎上,對文件中的每行獲取到的Class進行分析,並進行緩存 */ private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { //判斷配置的擴展類是否爲指定SPI接口的實現類 if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Error occurred when loading ... is not subtype of interface."); } if (clazz.isAnnotationPresent(Adaptive.class)) { //若擴展類有@Adaptive註解,將這個Class存入緩存「cachedAdaptiveClass」 //注意:一個SPI接口配置文件中,只能配置一個有@Adaptive註解的擴展類 cacheAdaptiveClass(clazz); } else if (isWrapperClass(clazz)) { //若擴展類具備clazz.getConstructor(type)這樣的構造器,則認爲其是一個具備包裝功能的擴展類, //並將其存入緩存「cachedWrapperClasses」,一個SPI接口能夠配置多個用於包裝的擴展類 cacheWrapperClass(clazz); } else { //進入到這個分支,表示該Class只是一個普通的SPI接口擴展類。 //判斷該擴展類是否具備無參構造器 clazz.getConstructor(); //若是該擴展類在SPI接口配置文件中未定義name,則判斷擴展類是否具備@Extension註解, //若是有,則以@Extension的value值做爲name;不然以擴展類的SimpleName做爲name, //而且,若是擴展類的SimpleName以SPI接口的SimpleName做爲後綴結尾,則name須要去掉該後綴 if (StringUtils.isEmpty(name)) { name = findAnnotationName(clazz); if (name.length() == 0) { throw new IllegalStateException("No such extension name for the class ..."); } } //對name按照","進行分割 String[] names = NAME_SEPARATOR.split(name); if (ArrayUtils.isNotEmpty(names)) { //names不爲空 //若擴展類具備@Activate註解,則使用names數組的第一個元素做爲key,@Activate的Class爲value //將映射關係存入緩存「cachedActivates」 cacheActivateClass(clazz, names[0]); for (String n : names) { //將該擴展類的name和Class存入緩存「cachedNames」,name保持爲names[0] cacheName(clazz, n); //將該擴展類的name和Class存入方法參數extensionClasses saveInExtensionClass(extensionClasses, clazz, name); } } } }
經過以上的源碼分析,咱們瞭解到getExtension方法獲取一個SPI接口的擴展類實例的流程分爲解析配置文件、加載並緩存擴展類、建立並加工擴展類實例幾個步驟。緩存
在獲得一個最終可用的擴展類實例前,該實例會進行屬性注入與層層包裝,這些行爲在官網上被成爲擴展點特性,這裏咱們把它稱之爲「擴展類特性」。官方給出了4個特性,分別爲「擴展點自動包裝」、「擴展點自動裝配」、「擴展點自適應」以及「擴展點自動激活」。在下面的其他章節中,咱們將重點來研究這幾個擴展點特性。併發
/** * 一個模擬的SPI接口 */ @SPI public interface WheelMaker { Wheel makeWheel(URL url); } /** * SPI接口的自適應擴展類 */ public class AdaptiveWheelMaker implements WheelMaker { //自適應擴展類該方法的邏輯爲經過URL中的參數,動態的獲取真正須要執行的SPI接口擴展類 public Wheel makeWheel(URL url) { if (url == null) { throw new IllegalArgumentException("url == null"); } // 1.從URL中獲取WheelMaker名稱 String wheelMakerName = url.getParameter("Wheel.maker"); if (wheelMakerName == null) { throw new IllegalArgumentException("wheelMakerName == null"); } // 2.經過SPI加載具體的WheelMaker WheelMaker wheelMaker = ExtensionLoader.getExtensionLoader(WheelMaker.class).getExtension(wheelMakerName); // 3.調用目標方法 return wheelMaker.makeWheel(URL url); } } /** * 一個模擬的SPI接口 */ @SPI public interface CarMaker { Car makeCar(URL url); } /** * 汽車製造者SPI接口的擴展類 */ public class RaceCarMaker implements CarMaker { WheelMaker wheelMaker; //在injectExtension方法中會經過setter注入AdaptiveWheelMaker //目前咱們只知道自動裝配行爲的結果,緣由會在「擴展點自動裝配」小節分析 public setWheelMaker(WheelMaker wheelMaker) { this.wheelMaker = wheelMaker; } //實現的方法 public Car makeCar(URL url) { //實際調用AdaptiveWheelMaker的makeWheel方法,得到當前運行環境參數url下須要使用的Wheel擴展類 Wheel wheel = wheelMaker.makeWheel(url); return new RaceCar(wheel, ...); } }
假設運行時傳入這樣一個url參數「dubbo://192.168.0.101:20880/XxxService?wheel.maker=MichelinWheelMaker」,那麼RaceCar最終的wheel爲擴展類「MichelinWheelMaker」製造出來的輪胎。app
/**************************************** 相關字段 ****************************************/ //緩存的自適應擴展類實例 private final Holder<Object> cachedAdaptiveInstance = new Holder<>(); //SPI接口的擴展類中具備@Adaptive註解的擴展類Class緩存 private volatile Class<?> cachedAdaptiveClass = null; //建立自適應擴展類實例的異常信息 private volatile Throwable createAdaptiveInstanceError; /**************************************** 相關方法 ****************************************/ /** * 獲取自適應擴展類的實例 */ public T getAdaptiveExtension() { //從ExtensionLoader的緩存字段中獲取數據,cachedAdaptiveInstance爲一個Holder<Object>對象 Object instance = cachedAdaptiveInstance.get(); if (instance == null) { //須要判斷一下建立自適應擴展對象的Throwable緩存是否存在,若是存在,直接拋出 if (createAdaptiveInstanceError == null) { //併發控制 synchronized (cachedAdaptiveInstance) { instance = cachedAdaptiveInstance.get(); if (instance == null) { try { //建立自適應擴展類實例 instance = createAdaptiveExtension(); //設置緩存 cachedAdaptiveInstance.set(instance); } catch (Throwable t) { createAdaptiveInstanceError = t; throw new IllegalStateException("......"); } } } } else { throw new IllegalStateException("......"); } } return (T) instance; } /** * 建立自適應擴展類實例 */ private T createAdaptiveExtension() { try { //(1)調用getAdaptiveExtensionClass方法獲取自適應擴展類的Class; //(2)經過newInstance實例化一個自適應擴展類的對象; //(3)調用injectExtension方法向自適應拓展類的實例中注入依賴,參考「擴展點自動裝配」小節; return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("......"); } } /** * 獲取自適應擴展類的Class */ private Class<?> getAdaptiveExtensionClass() { //獲取該ExtensionLoader對應SPI接口配置的全部擴展類(參考「getExtensionClsses」小節) getExtensionClasses(); //檢查具備@Adaptive註解的擴展類緩存,若緩存不爲空,則直接返回緩存 if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } //若是SPI接口配置的全部擴展類都沒有被@Adaptive標註,則建立自適應擴展類 return cachedAdaptiveClass = createAdaptiveExtensionClass(); } /** * 建立自適應擴展類 */ private Class<?> createAdaptiveExtensionClass() { //經過AdaptiveClassCodeGenerator的generate方法建立自適應擴展代碼 //該方法會檢測SPI接口中是否有被@Adapative註解的方法,對於要生成自適應擴展類的SPI接口 //必須至少包含一個被@Adaptive註解的方法,不然會拋出異常 String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate(); ClassLoader classLoader = findClassLoader(); //獲取編譯器實現類,同樣是經過AdaptiveExtension進行獲取,獲取以後進行編譯 org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); return compiler.compile(code, classLoader); }
經過getAdaptiveExtension方法的流程能夠發現,要想得到一個SPI接口的自適應擴展類實例,有2種方式:框架
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Adaptive { String[] value() default {}; }
能夠看到,@Adaptive註解既可使用在類上,又可使用在方法上。其包含一個字符串數組的屬性,在經過字節碼技術建立自適應擴展類時,該屬性參與到生成邏輯中,具體的建立邏輯咱們立刻經過下一節來了解。maven
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
首先傳入SPI接口的Class與默認的擴展類名稱(即@SPI註解的value值)建立一個AdaptiveClassCodeGenerator實例,以後調用generate方法生成代碼,AdaptiveClassCodeGenerator相對應的源碼以下:ide
/**************************************** 相關字段 ****************************************/ //SPI接口類型 private final Class<?> type; //SPI接口默認的擴展類名稱,即@SPI註解的value屬性值 private String defaultExtName; /**************************************** 相關方法 ****************************************/ /** * 能夠看到,構造器並未作太多的事情,只是簡單的將傳入的參數爲成員變量賦值 */ public AdaptiveClassCodeGenerator(Class<?> type, String defaultExtName) { this.type = type; this.defaultExtName = defaultExtName; } /** * 建立自適應擴展類的代碼串 */ public String generate() { //遍歷SPI接口是否具備@Adaptive註解修飾的方法,若是沒有則拋出異常 if (!hasAdaptiveMethod()) { throw new IllegalStateException("......"); } //按照一個JAVA類的代碼組成順序,拼接代碼字符串 StringBuilder code = new StringBuilder(); //拼接包信息字符串: "package" + SPI接口所在的包 code.append(generatePackageInfo()); //拼接import字符串: "import" + ExtensionLoader的全限定名 code.append(generateImports()); //拼接類開頭字符串: "public class" + SPI接口簡單名 + "$Adaptive implements" + SPI接口全限定名 + "{" //注意:使用全限定名的緣由爲import代碼串中只導入ExtensionLoader的類,下同 code.append(generateClassDeclaration()); //拼接每個方法字符串,邏輯比較複雜,在generateMethod方法源碼中詳細說明 Method[] methods = type.getMethods(); for (Method method : methods) { code.append(generateMethod(method)); } //拼接類結束字符串: "}" code.append("}"); return code.toString(); } /** * 檢測該SPI接口是否具備@Adaptive修飾的方法 */ private boolean hasAdaptiveMethod() { return Arrays.stream(type.getMethods()).anyMatch(m -> m.isAnnotationPresent(Adaptive.class)); } /** * 生成自適應擴展類的方法代碼串 */ private String generateMethod(Method method) { //獲取方法返回值類型全限定名 String methodReturnType = method.getReturnType().getCanonicalName(); //獲取方法名 String methodName = method.getName(); //獲取方法內容,有無@Adaptive修飾的方法其方法內容不一樣,詳細見下generateMethodContent源碼 String methodContent = generateMethodContent(method); //獲取方法參數列表,格式爲"參數類型全限定名 arg0, 參數類型全限定名 arg1, ..." String methodArgs = generateMethodArguments(method); //獲取方法異常拋出,格式爲"throws 異常1全限定名, 異常2全限定名, ..." String methodThrows = generateMethodThrows(method); //獲取一個方法代碼串 return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent); } /** * 生成自適應擴展類的方法體代碼串 */ private String generateMethodContent(Method method) { Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class); StringBuilder code = new StringBuilder(512); //判斷當前要生成的方法是否有@Adaptive註解 if (adaptiveAnnotation == null) { //對於沒有@Adaptive註解的方法,生成"throw new UnsupportedOperationException(...)"代碼 return generateUnsupported(method); } else { //檢查方法參數列表中是否有類型爲"com.apache.dubbo.common.URL"的參數 //簡單提一下,com.apache.dubbo.common.URL是dubbo框架中各組件的數據總線 int urlTypeIndex = getUrlTypeIndex(method); if (urlTypeIndex != -1) { //若是方法參數列表中有URL類型參數,則爲該參數生成判斷是否爲null以及賦值的代碼: //(generateUrlNullCheck方法比較簡單,不進行詳細的源碼分析,這裏只給出邏輯) //if (arg%d == null) throw new IllegalArgumentException("url == null"); //com.apache.dubbo.common.URL url = arg%d; //d的值爲urlTypeIndex code.append(generateUrlNullCheck(urlTypeIndex)); } else { //若是方法參數列表中沒有URL類型參數,則須要遍歷參數列表中每個參數的類型信息, //判斷哪個參數具備"public URL getXXX()"簽名形式的方法,若是有則中止遍歷且生成以下代碼: //(generateUrlAssignmentIndirectly方法比較簡單,不進行詳細的源碼分析,這裏只給出邏輯) //if (arg%d == null) 備註:此處的d爲具有"public URL getXXX()"方法的參數下標,從0開始 // throw new IllegalArgumentException("參數全限定名 + argument == null"); //if (arg%d.getter方法名() == null) // throw new IllegalArgumentException(參數全限定名 + argument getUrl() == null); //com.apache.dubbo.common.URL url = arg%d.getter方法名(); code.append(generateUrlAssignmentIndirectly(method)); } //獲取該方法的@Adaptive註解的屬性值value(爲一個String數組),這裏列出處理邏輯 //若是屬性值value爲非空數組,直接獲取數組內容便可; //若是value爲空數組,則需將SPI接口的類名按照駝峯命名法進行檢測,對每一個駝峯進行分割並插入"."號, //而後轉爲小寫,好比LoadBalance通過處理後,獲得load.balance String[] value = getMethodAdaptiveValue(adaptiveAnnotation); //判斷當前方法的參數列表中是否有類型爲org.apache.dubbo.rpc.Invocation的參數 boolean hasInvocation = hasInvocationArgument(method); //爲當前方法參數列表中第一個類型爲org.apache.dubbo.rpc.Invocation的參數生成判null語句以及調用語句 //if (arg%d == null) throw new IllegalArgumentException("invocation == null"); //String methodName = arg%d.getMethodName(); //%d是org.apache.dubbo.rpc.Invocation類型的參數在參數列表中的下標 code.append(generateInvocationArgumentNullCheck(method)); //使用前面獲取到的字符串數組value以及Invocation參數存在標識拼接獲取擴展類extName的代碼串, //這是自適應擴展類核心的代碼,由於自適應擴展類的目的就是要根據當前運行參數, //判斷應該獲取SPI接口的哪個擴展類,而ExtensionLoader.getExtension方法的參數就是這個extName //該方法邏輯比較複雜,判斷分支較多,詳細的分析在下面generateExtNameAssignment中 code.append(generateExtNameAssignment(value, hasInvocation)); //對上一步獲取的局部變量extName拼接其判null代碼 //if(extName == null) throw new IllegalStateException("Failed to get extension name from..."); code.append(generateExtNameNullCheck(value)); //生成獲取extName對應擴展類實例的代碼串,以下: //%s extension = (%<s)ExtensionLoader.getExtensionLoader(%s.class).getExtension(extName); //其中%s爲成員變量type.getName,即SPI接口的Class的全限定名 code.append(generateExtensionAssignment()); //生成目標方法調用邏輯,被@Adaptive註解的方法,第一個任務是根據運行時參數獲取對應的擴展類實例, //第二個任務就是調用這個實例的同名方法。生成的方法調用代碼串格式爲: //(1)若是該方法無返回值"extension.方法名(arg0, arg2, ..., argN);" //(2)若是該方法有返回值"return extension.方法名(arg0, arg1, ..., argN);" //其中extension爲上一步生成的擴展類實例變量名,arg0、arg1就爲當前@Adaptive註解方法的參數名 code.append(generateReturnAndInvocation(method)); } return code.toString(); } /** * 擴展名extName獲取代碼串 */ private String generateExtNameAssignment(String[] value, boolean hasInvocation) { String getNameCode = null; //反向遍歷value,目的是生成從URL中獲取拓展名的代碼,生成的代碼會賦值給getNameCode變量 for (int i = value.length - 1; i >= 0; --i) { //當遍歷的元素是最後一個元素時 if (i == value.length - 1) { //若默認擴展名不空(即@SPI註解的value值不空) if (null != defaultExtName) { //因爲protocol是URL的成員變量,可經過getProtocol方法獲取,其餘的則是從 //URL的成員變量parameters(一個Map<String, String>)中獲取。 //由於獲取方式不一樣,因此這裏要判斷value[i]是否爲protocol if (!"protocol".equals(value[i])) { //須要判斷一下hasInvocation標識(即當前方法是否有Invocation參數) if (hasInvocation) { //生成的代碼功能等價於下面的代碼: //url.getMethodParameter(methodName, value[i], defaultExtName) //注意,methodName是generateInvocationArgumentNullCheck生成的局部變量,下同 getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); } else { //生成的代碼功能等價於下面的代碼: //url.getParameter(value[i], defaultExtName) getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName); } } else { //生成的代碼功能等價於下面代碼: //( url.getProtocol() == null ? defaultExtName : url.getProtocol() ) getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName); } //若默認擴展名爲空 } else { if (!"protocol".equals(value[i])) { if (hasInvocation) { //生成的代碼格式同上,即 //url.getMethodParameter(methodName, value[i], defaultExtName) getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); } else { //生成的代碼功能等價於:url.getParameter(value[i]) getNameCode = String.format("url.getParameter(\"%s\")", value[i]); } } else { //生成的代碼功能等價於:url.getProtocol() getNameCode = "url.getProtocol()"; } } //當遍歷的元素不爲最後一個時 } else { if (!"protocol".equals(value[i])) { if (hasInvocation) { //生成的代碼同上,即: //url.getMethodParameter(methodName, value[i], defaultExtName) getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); } else { //在上一次獲取的getNameCode代碼結果基礎上,再次獲取getNameCode,即一層層的獲取值 //生成的代碼功能等價於下面的代碼: //url.getParameter(value[i], getNameCode) //以Transporter接口的connect方法爲例,最終生成的代碼以下: //url.getParameter("client", url.getParameter("transporter", "netty")) getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode); } } else { //在上一次獲取的getNameCode代碼結果基礎上,再次獲取getNameCode,即一層層的獲取值 //生成的代碼功能等價於下面的代碼: //url.getProtocol() == null ? getNameCode : url.getProtocol() //以Protocol接口的connect方法爲例,最終生成的代碼以下: //url.getProtocol() == null ? "dubbo" : url.getProtocol() getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode); } } } //返回生成extName的代碼:"String extName = getNameCode;" return String.format(CODE_EXT_NAME_ASSIGNMENT, getNameCode); }
ClassLoader classLoader = findClassLoader(); //獲取編譯器實現類,同樣是經過AdaptiveExtension進行獲取,獲取以後進行編譯 //在這裏具體獲取Compiler的細節略去,最終獲取到JavassistCompiler類的實例進行編譯 org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); return compiler.compile(code, classLoader);
對於這段代碼,咱們能夠運用本小節學到知識,來分析一下Compiler接口的自適應擴展類是什麼。
adaptive=org.apache.dubbo.common.compiler.support.AdaptiveCompiler jdk=org.apache.dubbo.common.compiler.support.JdkCompiler javassist=org.apache.dubbo.common.compiler.support.JavassistCompiler
一共配置了3個擴展類,根據name的名稱,能夠確定AdaptiveCompiler是具備@Adaptive註解的擴展類
@Adaptive public class AdaptiveCompiler implements Compiler { private static volatile String DEFAULT_COMPILER; public static void setDefaultCompiler(String compiler) { DEFAULT_COMPILER = compiler; } @Override public Class<?> compile(String code, ClassLoader classLoader) { Compiler compiler; ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class); String name = DEFAULT_COMPILER; // copy reference if (name != null && name.length() > 0) { compiler = loader.getExtension(name); } else { compiler = loader.getDefaultExtension(); } return compiler.compile(code, classLoader); } }
根據4.2小節中的代碼邏輯,在具備@Adaptive註解修飾的擴展類的前提下,自適應擴展類實例必定獲取到這個被修飾的類實例,所以「ExtensionLoader.getExtensionLoader(....Compiler.class).getAdaptiveExtension();」這段代碼獲取到了一個AdaptiveCompiler的實例。AdaptiveCompiler的compile方法很簡單,因爲成員變量「DEFAULT_COMPILER」始終爲null,其會調用ExtensionLoader的getDefaultExtension方法獲取默認的擴展類實例,即@SPI接口註解value值做爲name的實例,看看Compiler接口代碼:
@SPI("javassist") public interface Compiler { Class<?> compile(String code, ClassLoader classLoader); }
其默認的擴展類的name爲「javassist」,即類「org.apache.dubbo.common.compiler.support.JavassistCompiler」,這個類的具體compile方法很少贅述,就是校驗代碼字符串格式的正確性,並進行加載。
/** * 自動裝配擴展類實例 */ private T injectExtension(T instance) { try { //若是objectFactory不爲空,則進行自動裝配 if (objectFactory != null) { //遍歷擴展類實例的全部方法 for (Method method : instance.getClass().getMethods()) { //檢測方法是否以set開頭、只有一個參數、訪問修飾符爲public if (isSetter(method)) { //對於有@DisableInject的註解的setter方法,不須要注入 if (method.getAnnotation(DisableInject.class) != null) { continue; } //獲取setter方法的參數類型 Class<?> pt = method.getParameterTypes()[0]; //判斷參數類型是不是原始類型(基本類型+String+基本類型的包裝類+Date) if (ReflectUtils.isPrimitives(pt)) { continue; } try { //獲取要注入屬性名,好比setName方法中對應的屬性名爲name String property = getSetterProperty(method); //從objectFactory中根據屬性名與屬性的Class類型獲取依賴對象, //獲取到的是一個SPI接口的自適應擴展類的對象或者Spring環境下的一個bean, //objectFactory.getExtension方法的細節咱們在ExtensionFactory中將詳細討論 Object object = objectFactory.getExtension(pt, property); if (object != null) { //調用setter方法進行注入 method.invoke(instance, object); } } catch (Exception e) { logger.error("Failed to inject via method ..."); } } } } } catch (Exception e) { logger.error(e.getMessage(), e); } return instance; }
private ExtensionLoader(Class<?> type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
這幾行簡單的代碼包含了不少信息:首先,ExtensionFactory自身也是一個SPI接口,其一樣可以經過ExtensionLoader進行初始化。其次,當獲取一個SPI接口的ExtensionLoader時,若是該SPI接口爲ExtensionFactory,則不會設置objectFactory字段值,不然會經過getAdaptiveExtension方法拿到一個ExtensionFactory的實例並將字段objectFactory的值設置爲它。
@SPI public interface ExtensionFactory { <T> T getExtension(Class<T> type, String name); }
ExtensionFactory這個接口的繼承樹以下圖所示:
它有3個實現類,分別是AdaptiveExtensionFactory、SpiExtensionFactory、SpringExtensionFactory,相關的META-INFO配置文件內容以下:
#位於dubbo-common模塊下的META-INFO/dubbo/internal/com.apache.dubbo.common.ExtensionFactory文件 adaptive=org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory spi=org.apache.dubbo.common.extension.factory.SpiExtensionFactory #位於dubbo-spring模塊下的META-INF/dubbo/internal/com.apache.dubbo.common.ExtensionFactory文件 spring=org.apache.dubbo.config.spring.extension.SpringExtensionFactory
@Adaptive public class AdaptiveExtensionFactory implements ExtensionFactory { //維護了一組ExtensionFctory接口擴展類的實例 private final List<ExtensionFactory> factories; //構造方法,獲取全部ExtensionFactory配置的擴展類實例 public AdaptiveExtensionFactory() { //加載ExtensionFactory對應的ExtensionLoader ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class); List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();、 //getSupportedExtensions方法用於獲取ExtensionLoader的cachedClasses緩存的keySet //即SPI接口配置文件中name的Set集合 for (String name : loader.getSupportedExtensions()) { //根據name名字,調用getExtension方法獲取對應擴展類的實例並緩存 list.add(loader.getExtension(name)); } factories = Collections.unmodifiableList(list); } @Override public <T> T getExtension(Class<T> type, String name) { for (ExtensionFactory factory : factories) { //調用每個ExtensionFactory實現類的getExtension方法,並返回第一個不爲null的對象 T extension = factory.getExtension(type, name); if (extension != null) { return extension; } } return null; } }
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); //getSupportedExtensions方法用於獲取ExtensionLoader的cachedClasses緩存的keySet //即SPI接口配置文件中name的Set集合 if (!loader.getSupportedExtensions().isEmpty()) { return loader.getAdaptiveExtension(); } } return null; } }
public class SpringExtensionFactory implements ExtensionFactory { private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class); private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>(); private static final ApplicationListener SHUTDOWN_HOOK_LISTENER = new ShutdownHookListener(); public static void addApplicationContext(ApplicationContext context) { CONTEXTS.add(context); if (context instanceof ConfigurableApplicationContext) { ((ConfigurableApplicationContext) context).registerShutdownHook(); DubboShutdownHook.getDubboShutdownHook().unregister(); } //先SpringContext註冊dubbo關閉鉤子 BeanFactoryUtils.addApplicationListener(context, SHUTDOWN_HOOK_LISTENER); } //省略了一些CONTEXTS的CRUD方法... @Override @SuppressWarnings("unchecked") public <T> T getExtension(Class<T> type, String name) { //不支持被@SPI註解標註的接口類型 if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { return null; } //首先根據name從Spring上下文獲取bean,並驗證該bean與type是否一致 for (ApplicationContext context : CONTEXTS) { if (context.containsBean(name)) { Object bean = context.getBean(name); if (type.isInstance(bean)) { return (T) bean; } } } logger.warn("No spring extension (bean) named ..."); //若是type的類型爲Object,直接返回null if (Object.class == type) { return null; } //嘗試根據type從Spring上下文獲取bean,若是type對應的bean並不是惟一,直接報錯 for (ApplicationContext context : CONTEXTS) { try { return context.getBean(type); } catch (NoUniqueBeanDefinitionException multiBeanExe) { logger.warn("Find more than 1 spring extensions (beans) of type ..."); } catch (NoSuchBeanDefinitionException noBeanExe) { if (logger.isDebugEnabled()) { logger.debug("Error when get spring extension(bean) for type: ..."); } } } logger.warn("No spring extension (bean) named: ..."); //未找到,返回null return null; } private static class ShutdownHookListener implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ContextClosedEvent) { DubboShutdownHook shutdownHook = DubboShutdownHook.getDubboShutdownHook(); shutdownHook.doDestroy(); } } } }
SpringExtensionFactory維護了Spring上下文集合「Set<ApplicationContext>」。在getExtension方法中,經過Spring上下文去獲取實例。而且,SpringExtensionFactory在設置SpringContext時,會向獲取到的Context註冊一個Spring容器關閉事件的監聽鉤子,用於關閉dubbo。
package com.alibaba.xxx; import org.apache.dubbo.rpc.Protocol; public class XxxProtocolWrapper implements Protocol { Protocol impl; //單參數構造函數,且參數類型爲其實現的SPI接口類型Protocol public XxxProtocolWrapper(Protocol protocol) { impl = protocol; } // 接口方法作一個操做後,再調用extension的方法 public void refer() { //... 一些操做 impl.refer(); // ... 一些操做 } }
自動包裝的機制可以近似的實現AOP的功能,經過Wrapper類能夠把全部擴展點公共邏輯移至Wrapper中,新加的Wrapper在全部的擴展點上添加了邏輯。