Dubbo原始碼分析(一)Dubbo的擴充套件點機制

狼圖騰xxx發表於2018-10-01

寫在前面的話

自己用Dubbo也有幾年時間,一直沒有讀過Dubbo的原始碼,現在來讀一讀Dubbo的原始碼,分析一下Dubbo的幾個核心,並寫一個Dubbo的原始碼專題來記錄一下學習過程,供大家參考,寫的不好的地方,歡迎拍磚
專題分為以下幾個部分:

PS:讀原始碼前先掌握以下基礎

  1. JDK的SPI
  2. Java多執行緒/執行緒池基礎
  3. Javasissit基礎(動態編譯)
  4. Netty基礎
  5. Zookeeper基礎,zkClient客戶端API
  6. 工廠模式,裝飾模式,模板模式,單例模式,動態代理模式
  7. Spring的schema自定義擴充套件
  8. 序列化

PS:讀原始碼前的建議

  1. 程式碼量很大,各個地方都有關聯,慢慢讀,不要著急,一遍不行就兩遍,兩遍不行就三遍,總有看懂的時候
  2. 帶著問題去看,先想想這段程式碼的目的是什麼,解決了什麼問題
  3. 沿著一條主線讀,不影響流程走向的程式碼可以略過

Dubbo的擴充套件點

為什麼先讀擴充套件點

之所以選擇先從Dubbo的擴充套件點機制入手,因為Dubbo的整體架構設計上,都是通過擴充套件點去實現,先了解清楚這塊內容,才能讀懂程式碼。

Dubbo擴充套件點規範

  • 如果要擴充套件自定義的SPI,可以在resources目錄下配置三種目錄,分別是:META-INF/dubbo/ 或者 META-INF/services/ 或者 META-INF/dubbo/internal/
  • 檔名稱和介面名稱保持一致,檔案內容為key=vaule的形式xxx=com.alibaba.xxx.XxxProtocol
  • 舉個栗子:如果我們要擴充套件Dubbo的一個協議,在META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol這個檔案裡面增加一行自己的擴充套件:xxx=com.alibaba.xxx.XxxProtocol,在Dubbo的配置檔案<dubbo:protocol name="xxx" />,這樣就可以實現自定義的Dubbo協議

Dubbo的擴充套件點和JDK的SPI的區別

Dubbo的擴充套件點(Extension)在JDK的SPI思想的基礎上做了一些改進:

  • Dubbo的設計中用到了大量的全域性快取,所有的Extension都快取在cachedInstances中,該物件型別為ConcurrentMap<String, Holder>
  • Dubbo的擴充套件點支援預設實現,比如,Protocol中的@SPI("dubbo")預設為DubboProtocol,預設擴充套件點可以通過ExtensionLoader.getExtensionLoader(Protocol.class).getDefaultExtension()獲取
  • Dubbo的擴充套件點可以動態獲取擴充套件物件,比如:ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName)來獲取擴充套件物件,我覺得這是Dubbo擴充套件點設計的很有意思的地方,非常的靈活方便,程式碼中大量的用到了這個方法
  • Dubbo的擴充套件點提供了AOP功能,在cachedWrapperClasses中,在原來的SPI的類上包裝了XxxxFilterWrapper XxxxListenerWrapper
  • Dubbo擴充套件點提供了IOC功能,通過建構函式注入所需的例項,後面原始碼會分析
  • 讀原始碼

    • 先想想Dubbo的SPI的目的是什麼?獲取一個我們所需要的指定的物件
    • 怎麼獲取呢?ExtensionLoader.getExtension(String name)
      我們先從這段程式碼入手,這個程式碼的作用就是獲取一個自適應的擴充套件類,我們看下這段程式碼的整個執行流程:
ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
複製程式碼

getExtensionLoader方法中傳入的了一個Protocol,我們看下Protocol長啥樣

@SPI("dubbo")
public interface Protocol {
   //獲取預設埠,當使用者沒有配置埠時使用。
   int getDefaultPort();

  // 暴露遠端服務:<br>
   @Adaptive
   <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

   //引用遠端服務:<br>
   @Adaptive
   <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

   //釋放協議:<br>
   void destroy();
複製程式碼

這是一個協議介面,在類上有@SPI註解,有個預設值dubbo,在方法上有@Adaptive註解,這兩個註解是什麼作用呢?我們繼續往下看getExtensionLoader方法:

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
·····
       //先從快取中取值,為null,則去new一個ExtensionLoader
       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;
   }
複製程式碼

繼續進入new ExtensionLoader(type)方法:

private ExtensionLoader(Class<?> type) {
       this.type = type;
       objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
   }
複製程式碼

給type變數賦值,objectFactory賦值,此時傳入的type是Protcol,繼續執行ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()方法,這裡我們分為兩個步驟:
步驟一:ExtensionLoader.getExtensionLoader(ExtensionFactory.class),此時type為ExtensionFactory.class,這段程式碼我們得到一個ExtensionLoader例項,這個ExtensionLoader例項中,objetFactory為null, 步驟二:getAdaptiveExtension()方法,為cachedAdaptiveInstance賦值,我們來看這個方法:

  public T getAdaptiveExtension() {
       Object instance = cachedAdaptiveInstance.get();
       if (instance == null) {
           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("fail to create adaptive instance: " + t.toString(), t);
                       }
                   }
               }
           } else {
               throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
           }
       }
複製程式碼

雙重檢查鎖判斷快取,如果沒有則進入createAdaptiveExtension()方法,這個方法有兩部分,一個是injectExtension,一個是getAdaptiveExtensionClass().newInstance()

private T createAdaptiveExtension() {                                                                                            
 try {                                                                                                                        
     return injectExtension((T) getAdaptiveExtensionClass().newInstance());                                                   
 } catch (Exception e) {                                                                                                      
     throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);         
 }                                                                                                                            
}                                                                                                                                
複製程式碼

先看getAdaptiveExtensionClass(),獲取一個介面卡擴充套件點的類

private Class<?> getAdaptiveExtensionClass() {                                                      
 getExtensionClasses();                                                                          
 if (cachedAdaptiveClass != null) {                                                              
     return cachedAdaptiveClass;                                                                 
 }                                                                                               
 return cachedAdaptiveClass = createAdaptiveExtensionClass();                                    
}                                                                                                   
複製程式碼

這裡我們進入getExtensionClasses()方法,雙重檢查鎖判斷,如果沒有,繼續

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()方法

    // 此方法已經getExtensionClasses方法同步過。
    private Map<String, Class<?>> loadExtensionClasses() {
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if (value != null && (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];
            }
        }

        Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
        loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        loadFile(extensionClasses, DUBBO_DIRECTORY);
        loadFile(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
    }
複製程式碼

這裡有個type.getAnnotation(SPI.class),這個type就是剛剛再初始化ExtensionLoader的時候傳入的,我們先看type=ExtensionFactory.class的情況,ExtensionFactory介面類上有@SPI註解,但是value為空,然後三次呼叫loadFile方法,分別對應Dubbo擴充套件點的三個配置檔案路徑,在原始碼中我們可以找到ExtensionFactory對應的檔案,

Dubbo原始碼分析(一)Dubbo的擴充套件點機制

Dubbo原始碼分析(一)Dubbo的擴充套件點機制

通過loadFile方法,最終extensionClasses返回SpringExtensionFactory和SpiExtensionFactory 快取到cachedClasses中,為什麼只返回了2個類呢,AdaptiveExtensionFactory為什麼沒有返回呢,因為在loadFile中AdaptiveExtensionFactory因為類上有@Adaptive註解,所以直接快取到cachedAdaptiveClass中(此時,我們要思考,@Adaptive註解放在類上和放在方法上有什麼區別),我們看下loadFile中的關鍵程式碼

private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
····
       // 1.判斷當前class類上面有沒有Adaptive註解,如果有,則直接賦值給cachedAdaptiveClass
       if (clazz.isAnnotationPresent(Adaptive.class)) {
           if (cachedAdaptiveClass == null) {
               cachedAdaptiveClass = clazz;
           } else if (!cachedAdaptiveClass.equals(clazz)) {
               throw new IllegalStateException("More than 1 adaptive class found: "
                       + cachedAdaptiveClass.getClass().getName()
                       + ", " + clazz.getClass().getName());
           }
       } else {
           //2.如果沒有類註解,那麼判斷該class中沒有引數是type的構造方法,如果有,則把calss放入cachedWrapperClasses中
           try {
               clazz.getConstructor(type);
               Set<Class<?>> wrappers = cachedWrapperClasses;
               if (wrappers == null) {
                   cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                   wrappers = cachedWrapperClasses;
               }
               wrappers.add(clazz);
           } catch (NoSuchMethodException e) {
               //3.判斷是否有預設構造方法
               clazz.getConstructor();
               if (name == null || name.length() == 0) {
                   name = findAnnotationName(clazz);
                   if (name == null || name.length() == 0) {
                       if (clazz.getSimpleName().length() > type.getSimpleName().length()
                               && clazz.getSimpleName().endsWith(type.getSimpleName())) {
                           name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
                       } else {
                           throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
                       }
                   }
               }
               String[] names = NAME_SEPARATOR.split(name);
               if (names != null && names.length > 0) {
                   //4.判斷class是否有@Activate註解,如果有,則放入cachedActivates
                   Activate activate = clazz.getAnnotation(Activate.class);
                   if (activate != null) {
                       cachedActivates.put(names[0], activate);
                   }
                   for (String n : names) {
                       //5.快取calss到cachedNames中
                       if (!cachedNames.containsKey(clazz)) {
                           cachedNames.put(clazz, n);
                       }
                       Class<?> c = extensionClasses.get(n);
                       if (c == null) {
                           extensionClasses.put(n, clazz);
                       } else if (c != clazz) {
                           throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                       }
                   }
               }
           }
       }
   }
   }
複製程式碼

至此,我們已經拿到了extensionClasses,並快取到了cachedClasses中,回到getAdaptiveExtensionClass()方法中

private Class<?> getAdaptiveExtensionClass() {
       getExtensionClasses();
       if (cachedAdaptiveClass != null) {
           return cachedAdaptiveClass;
       }
       return cachedAdaptiveClass = createAdaptiveExtensionClass();
   }
複製程式碼

如果cachedAdaptiveClass不為空,那麼就返回cachedAdaptiveClass,剛剛我們在loadFile()方法中講過,@Adaptive註解在類上,那麼就會快取到cachedAdaptiveClass中,這個時候cachedAdaptiveClass有值,為AdaptiveExtensionFactory,所以這裡直接返回AdaptiveExtensionFactory,繼續返回createAdaptiveExtension()方法,剛剛我們只是走完了createAdaptiveExtension()方法中的一個部分,還有injectExtension方法,這個方法是幹什麼的,在type=ExtensionFactory.class流程中,這個方法的作用沒有體現,先不看injectExtension,我們放在後面的流程去看,然後繼續返回到getAdaptiveExtension方法中,把例項AdaptiveExtensionFactory快取到cachedAdaptiveInstance中,繼續返回到ExtensionLoader方法中

private ExtensionLoader(Class<?> type) {
       this.type = type;
       objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
   }
複製程式碼

這個時候,objectFactory已經有值了,就是AdaptiveExtensionFactory,繼續返回getExtensionLoader方法

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
       ····
       //EXTENSION_LOADERS判斷是否有type,ConcurrentMap<Class<?>, ExtensionLoader<?>>
       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例項快取到EXTENSION_LOADERS中,此時type=Protocol

ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
複製程式碼

至此,我們已經執行完了ExtensionLoader.getExtensionLoader(Protocol.class),得到了ExtensionLoader例項,繼續執行getAdaptiveExtension()方法,這個方法在上面已經分析過了,我們再看下跟type=ExtensionFactory的時候有什麼區別,先看下com.alibaba.dubbo.rpc.Protocol檔案中有哪些擴充套件點(這個檔案在原始碼中是分散的,可以在Dubbo的jar包中找,jar包中是合併的)

Dubbo原始碼分析(一)Dubbo的擴充套件點機制
一共有13個擴充套件點,其中有2個Wrapper包裝類,我們直接看loadFile方法,extensionClasses返回了11條記錄

Dubbo原始碼分析(一)Dubbo的擴充套件點機制

這個時候再看下當前記憶體中的資料

Dubbo原始碼分析(一)Dubbo的擴充套件點機制
cachedNames中有11條,cachedWrapperClasses中有2條分別是ProtocolListenerWrapper 和 ProtocolFilterWrapper ,cachedClasses中有11條,回到getAdaptiveExtensionClass()方法中,我們在上面說過,Protocol的類上有 @SPI("dubbo")註解,export和refer上有@Adaptive註解,所以此時cachedAdaptiveClass是null, ,進入createAdaptiveExtensionClass()方法,這個方法的目的是自動生成和編譯一個動態代理介面卡類,名字叫Protocol$Adaptive, 這裡又用到了一個Compile擴充套件點,可以看到,這裡用到了ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(),有木有很熟悉,這裡得到一個AdaptiveCompiler(因為AdaptiveCompiler類上有@Adaptive註解)

private Class<?> createAdaptiveExtensionClass() {
   //生成位元組碼檔案
   String code = createAdaptiveExtensionClassCode(); 
   //獲得類載入器
   ClassLoader classLoader = findClassLoader(); 
   com.alibaba.dubbo.common.compiler.Compiler compiler = 
   ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
   //動態編譯
   return compiler.compile(code, classLoader);                         
}                                                                                                                                           
複製程式碼

執行compiler.compile(code, classLoader),先看下AdaptiveCompiler類

@Adaptive
public class AdaptiveCompiler implements Compiler {

   private static volatile String DEFAULT_COMPILER;

   public static void setDefaultCompiler(String compiler) {
       DEFAULT_COMPILER = compiler;
   }

   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);
   }
}
複製程式碼

這裡的DEFAULT_COMPILER值為JavassistCompiler,執行loader.getExtension(name),這個方法這裡暫時不展開,結果是得到JavassistCompiler例項,這裡是一個裝飾模式的設計,最終呼叫JavassistCompiler.compile()方法得到Protocol$Adpative,

回到我們最初的程式碼的入口

ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
複製程式碼

這句程式碼就最終的返回結果就是Protocol$Adpative,我們把這個代理類拿出來看一下

package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
  public void destroy() {throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
  }
  public int getDefaultPort() {throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
  }
  public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
      if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
      if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
      String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
      if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
      com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
      return extension.export(arg0);
  }
  public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
      if (arg1 == null) throw new IllegalArgumentException("url == null");
      com.alibaba.dubbo.common.URL url = arg1;
      String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
      if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
      com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
      return extension.refer(arg0, arg1);
  }
}

複製程式碼

這個時候如果執行Protocol$Adpative.export方法,我們看下這個介面卡代理類裡面的export()方法,通過url來獲取extName,所以Dubbo是基於URL來驅動的, 看到Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName)這個方法,這個方法是不是又又又很熟悉,接下來我們來分析getExtension(String name)方法,假設此時extName=dubbo

public T getExtension(String name) {
        if (name == null || name.length() == 0)
            throw new IllegalArgumentException("Extension name == null");
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        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) {
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }
複製程式碼

進入createExtension()方法

private T createExtension(String name) {
        //1.通過name獲取ExtensionClasses,此時為DubboProtocol
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
            //2.獲取DubboProtocol例項
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            //3.dubbo的IOC反轉控制,就是從spi和spring裡面提取物件賦值。
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (wrapperClasses != null && wrapperClasses.size() > 0) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    //4.如果是包裝類
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                    type + ")  could not be instantiated: " + t.getMessage(), t);
        }
    }

複製程式碼

第三步injectExtension(instance),看一下程式碼:

private T injectExtension(T instance) {
        try {
            if (objectFactory != null) {
                //1.拿到所有的方法
                for (Method method : instance.getClass().getMethods()) {
                    //判斷是否是set方法
                    if (method.getName().startsWith("set")
                            && method.getParameterTypes().length == 1
                            && Modifier.isPublic(method.getModifiers())) {
                        Class<?> pt = method.getParameterTypes()[0];
                        try {
                            String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                            //從objectFactory中獲取所需要注入的例項
                            Object object = objectFactory.getExtension(pt, property);
                            if (object != null) {
                                method.invoke(instance, object);
                            }
                        } catch (Exception e) {
                            logger.error("fail to inject via method " + method.getName()
                                    + " of interface " + type.getName() + ": " + e.getMessage(), e);
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return instance;
    }
複製程式碼

這個方法就是Dubbo完成依賴注入的地方,到這裡關於Dubbo的擴充套件點機制的程式碼就分析完成了。

總結

  • 為什麼要設計Adaptive?
    Adaptive設計的目的是為了識別固定已知類和擴充套件未知類
  • 註解在類上和註解在方法上的區別?
    1.註解在類上:代表人工實現,實現一個裝飾類(設計模式中的裝飾模式),它主要作用於固定已知類, 目前整個系統只有2個,AdaptiveCompiler、AdaptiveExtensionFactory。
    • 為什麼AdaptiveCompiler這個類是固定已知的?因為整個框架僅支援Javassist和JdkCompiler。
    • 為什麼AdaptiveExtensionFactory這個類是固定已知的?因為整個框架僅支援2個objFactory,一個是spi,另一個是spring
      2.註解在方法上:代表自動生成和編譯一個動態的Adpative類,它主要是用於SPI,因為spi的類是不固定、未知的擴充套件類,所以設計了動態XXX$Adaptive類. 例如 Protocol的spi類有 injvm dubbo registry filter listener等等 很多擴充套件未知類, 它設計了Protocol$Adaptive的類,通過ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(spi類);來提取物件

相關文章