Dubbo源碼解析(二)---Extension 機制

一 什麼是Extension 機制

Dubbo的類加載機制是模仿jdkspi加載機制;

 JdkSPI擴展加載機制:約定是當服務的提供者每增加一個接口的實現類時,需要在jar包的META-INF/service/目錄下同時創建一個以服務接口命名的具體實現類,該文件裏面就是保存的實現該接口的具體實現類。而當外部程序裝配這個模塊的時候,就能通過該jar包META-INF/services/裏的配置文件找到具體的實現類名,並裝載實例化,完成模塊的注入。 基於這樣一個約定就能很好的找到服務接口的實現類,而不需要再代碼裏制定。jdk提供服務實現查找的一個工具類:java.util.ServiceLoader。

比如:

jdbc:在jdbc4以前,開發人員還需要基於Class.forName("xxx")的方式來裝載驅動。jdbc4也基於spi的機制來發現驅動提供商了,可以通過META-INF/services/java.sql.Driver文件裏指定實現類的方式來暴露驅動提供者如下圖:

 

但是原始的jdk擴展點加載機制有些缺陷:

·JDK標準的SPI會一次性實例化擴展點所有實現,如果有擴展實現初始化很耗時,但如果沒用上也加載,會很浪費資源。

·如果擴展點加載失敗,連擴展點的名稱都拿不到了。比如:JDK標準的ScriptEngine,通過getName();獲取腳本類型的名稱,但如果RubyScriptEngine因爲所依賴的jruby.jar不存在,導致RubyScriptEngine類加載失敗,這個失敗原因被吃掉了,和ruby對應不起來,當用戶執行ruby腳本時,會報不支持ruby,而不是真正失敗的原因。

·Dubbo增加了對擴展點IoC和AOP的支持,一個擴展點可以直接setter注入其它擴展點。

理解Dubbo的SPI機制之前的幾個概念:

·擴展點:Dubbo作爲一個非常靈活的框架,並不會強制所有用戶必須使用Dubbo框架裏自帶的某些架構,比如註冊中心的話,dubbo提供了zookeeper和redis,而開發者可以根據自己的需要使用自定的註冊中心,針對這種可靈活替換的技術我們就稱之爲擴展點技術,類似擴展點在Dubbo中有很多,比如Protocol,Filter,LoadBlance,Cluster等等;

·Wrapper:Dubbo在加載某個接口的擴展類時候,如果發現某個實現中有一個拷貝構造函數,那麼該接口實現是就是該接口的包裝類,此時dubbo會在真正的實現類上包裝一層wrapper,比如ProtocolFilterWrapper中含有Protocol引用,及構造函數,所以加載filter配置時會返回wrapper類;即這個時候從ExtensionLoader中返回的實際擴展類是被Wrapper包裝的接口實現類。

@SPI  註解 :可以認爲是定義默認實現類;
比如 Protocol 接口中,定義默認協議時 dubbo;
@SPI("dubbo")
public interface Protocol {}

@Adaptive 註解:該註解打在接口方法上;調 ExtensionLoader.getAdaptiveExtension()獲取設配類,會先通過前面的過程生成 java 的源代碼,在通過編譯器編譯成 class 加載。但是 Compiler 的實現策略選擇也是通過 通過編譯器編譯成 class 文件那豈不是要死循環下去了嗎?

此時分析 ExtensionLoader.getAdaptiveExtension()函數,對於有實現類上去打了註解 @Adaptive的dubbo spi擴展機制,它獲取設配類不在通過前面過程生成設配類java源代碼, 而是在讀取擴展文件的時候遇到實現類打了註解@Adaptive 就把這個類作爲設配類緩存在 ExtensionLoader 中,調用是直接返回。

·擴展點自動激活:可以理解爲條件激活,比如Filter接口有很多擴展點實現類,當想簡化配置用到哪些過濾器時,可以@Activate自動激活,或者配置爲@Activate(“xxx”)條件配置激活。

二 ExtensionLoader過程詳解

Dubbo的擴展點加載機制中,ExtensionLoader是整個SPI加載的核心,而在DubboExtensionLoader的調用一般如下:

    private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);

Container:

@SPI("spring")
public interface Container {
    
    /**
     * start.
     */
    void start();
    
    /**
     * stop.
     */
    void stop();

}

基於以下啓動類的流程進行ExtensionLoader過程講解(這種加載機制dubbo用的很多,後面的proctol也用到,爲了條理清晰點,從啓動開始梳理):

public class DemoProvider {

    public static void main(String[] args) {
        com.alibaba.dubbo.container.Main.main(args);
    }

}

main啓動類:

/*
 * Copyright 1999-2011 Alibaba Group.
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.dubbo.container;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.common.utils.ConfigUtils;

/**
 * Main. (API, Static, ThreadSafe)
 * 
 * @author william.liangf
 */
public class Main {

    public static final String CONTAINER_KEY = "dubbo.container";

    public static final String SHUTDOWN_HOOK_KEY = "dubbo.shutdown.hook";
    
    private static final Logger logger = LoggerFactory.getLogger(Main.class);
    
    //獲取容器類的擴展點加載器
    private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);
    
    private static volatile boolean running = true;

    public static void main(String[] args) {
        try {
            if (args == null || args.length == 0) {
                String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName());
                args = Constants.COMMA_SPLIT_PATTERN.split(config);
            }
            
            final List<Container> containers = new ArrayList<Container>();
            for (int i = 0; i < args.length; i ++) {
                containers.add(loader.getExtension(args[i]));
            }
            logger.info("Use container type(" + Arrays.toString(args) + ") to run dubbo serivce.");
            
            if ("true".equals(System.getProperty(SHUTDOWN_HOOK_KEY))) {
	            Runtime.getRuntime().addShutdownHook(new Thread() {
	                public void run() {
	                    for (Container container : containers) {
	                        try {
	                            container.stop();
	                            logger.info("Dubbo " + container.getClass().getSimpleName() + " stopped!");
	                        } catch (Throwable t) {
	                            logger.error(t.getMessage(), t);
	                        }
	                        synchronized (Main.class) {
	                            running = false;
	                            Main.class.notify();
	                        }
	                    }
	                }
	            });
            }
            
            for (Container container : containers) {
                container.start();
                logger.info("Dubbo " + container.getClass().getSimpleName() + " started!");
            }
            System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date()) + " Dubbo service server started!");
        } catch (RuntimeException e) {
            e.printStackTrace();
            logger.error(e.getMessage(), e);
            System.exit(1);
        }
        synchronized (Main.class) {
            while (running) {
                try {
                    Main.class.wait();
                } catch (Throwable e) {
                }
            }
        }
    }
    
}

1. 獲取擴展點加載器的方法

 private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);
  private static <T> boolean withExtensionAnnotation(Class<T> type) {
 
        return type.isAnnotationPresent(SPI.class);
    }
    
    @SuppressWarnings("unchecked")
    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    	//1.判斷該class是否爲空
        if (type == null)
            throw new IllegalArgumentException("Extension type == null");
        //2.判斷該class是否爲接口類型
        if(!type.isInterface()) {
            throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
        }
        //3.判斷該class是否是@SPI註解類型
        
        if(!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type(" + type + 
                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
        //創建一個class對應的擴展點加載程序
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
       //如果該loader爲null,則創建一個class對應的extensionloader加載器放入EXTENSION_LOADERS中,
       // 並且返回loader
        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;
        // EXTENSION_LOADERS 靜態的線程安全map,每個class類對應一個extensionloader加載器
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

getAdaptiveExtension()::

    @SuppressWarnings("unchecked")
    public T getAdaptiveExtension() {
    	//獲取一個自適應緩存對象
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
            if(createAdaptiveInstanceError == null) {
                synchronized (cachedAdaptiveInstance) {
                    instance = cachedAdaptiveInstance.get();
                    if (instance == null) {
                        try {
                        	//沒有就自行創建一個AdaptiveExtension對象
                            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);
            }
        }

        return (T) instance;
    }

createAdaptiveExtension():

    @SuppressWarnings("unchecked")
    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);
        }
    }
    
    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;
	}

injectExtension():

//爲生成的 instance 注入變量;
//其目標是搜索所有 set 開頭,同時只有一個入參的函數,執行該函數,對變量進行注入  
private T injectExtension(T instance) {
        try {
            if (objectFactory != null) {
                for (Method method : instance.getClass().getMethods()) {
                    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) : "";
                            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;
    }
    

2.加載指定文件裏的key-value值 例如:spring=com.alibaba.dubbo.container.spring.SpringContainer

loadExtensionClasses()

    // 此方法已經getExtensionClasses方法同步過。
    private Map<String, Class<?>> loadExtensionClasses() {
    	//1.獲取註解爲@SPI的類
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if(defaultAnnotation != null) {
        	//2.獲取@SPI("")中的值
            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<?>>();
        //加載META-INF/dubbo/internal/下的文件內容
        loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        //加載META-INF/dubbo/下的文件內容
        loadFile(extensionClasses, DUBBO_DIRECTORY);
        //加載META-INF/services/下的文件內容
        loadFile(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
    }
    

loadFile():

 private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
        String fileName = dir + type.getName();
        try {
        	//Enumeration(枚舉)接口的作用和Iterator類似,
        	//只提供了遍歷Vector和HashTable類型集合元素的功能,不支持元素的移除操作
            Enumeration<java.net.URL> urls;
            //創建一個ExtensionLoader加載器
            ClassLoader classLoader = findClassLoader();
            if (classLoader != null) {
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }
            if (urls != null) {
            	//遍歷Enumeration
                while (urls.hasMoreElements()) {
                    java.net.URL url = urls.nextElement();
                    try {
                  //讀取url文件中的內容
                        BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
                        try {
                            String line = null;
                            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) {
                                            Class<?> clazz = Class.forName(line, true, classLoader);
                                            //判定此 Class 對象所表示的類或接口與指定的 Class 參數所表示的類或接口是否相同,
                                            //或是否是其超類或超接口。如果是則返回 true;否則返回 false。
                                            //如果該 Class 表示一個基本類型,且指定的 Class 參數正是該 Class 對象,則該方法返回 true;否則返回 false。 
                                            if (! type.isAssignableFrom(clazz)) {
                                                throw new IllegalStateException("Error when load extension class(interface: " +
                                                        type + ", class line: " + clazz.getName() + "), class " 
                                                        + clazz.getName() + "is not subtype of interface.");
                                            }
                                            //isAnnotationPresent判斷是否有@Adative
                                            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 {
                                                try {
                                                    clazz.getConstructor(type);
                                                    Set<Class<?>> wrappers = cachedWrapperClasses;
                                                    if (wrappers == null) {
                                                        cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                                                        wrappers = cachedWrapperClasses;
                                                    }
                                                    wrappers.add(clazz);
                                                } catch (NoSuchMethodException e) {
                                                	//獲得構造器(Constructor)對象並調用其newInstance()方法創建對象
                                                    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) {
                                                    	//@Activate它有兩個設置過濾條件的字段,group,value 都是字符數組。用來指定這個擴展類在什麼條件下激活
                                                        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) {
                                                                extensionClasses.put(n, clazz);
                                                            } else if (c != clazz) {
                                                                throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    } catch (Throwable t) {
                                        IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);
                                        exceptions.put(line, e);
                                    }
                                }
                            } // end of while read lines
                        } finally {
                            reader.close();
                        }
                    } catch (Throwable t) {
                        logger.error("Exception when load extension class(interface: " +
                                            type + ", class file: " + url + ") in " + url, t);
                    }
                } // end of while urls
            }
        } catch (Throwable t) {
            logger.error("Exception when load extension class(interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }
    

extensionClasses的結果爲:

{spring=class com.alibaba.dubbo.container.spring.SpringContainer, jetty=class com.alibaba.dubbo.container.jetty.JettyContainer, log4j=class com.alibaba.dubbo.container.log4j.Log4jContainer}

3.獲取dubbo.properties的屬性值

  if (args == null || args.length == 0) {
            	//獲取加載器中指定爲dubbo.container的值
                String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName());
                //將參數進行格式處理
                args = Constants.COMMA_SPLIT_PATTERN.split(config);
            }
            
        
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static String getProperty(String key, String defaultValue) {
       //先從系統參數中尋找是否有dubbo.container的key
        String value = System.getProperty(key);
        if (value != null && value.length() > 0) {
            return value;
        }
        //否則從自定義的properties中獲取
        Properties properties = getProperties();
        return replaceProperty(properties.getProperty(key, defaultValue), (Map)properties);
    }

getProperties():

    public static Properties getProperties() {
        if (PROPERTIES == null) {
            synchronized (ConfigUtils.class) {
                if (PROPERTIES == null) {
                	//默認在項目路徑下去尋找dubbo.properties.file的進程變量值
                    String path = System.getProperty(Constants.DUBBO_PROPERTIES_KEY);
                    if (path == null || path.length() == 0) {
                    	//尋找dubbo.properties.file的環境變量值
                        path = System.getenv(Constants.DUBBO_PROPERTIES_KEY);
                        if (path == null || path.length() == 0) {
                        	//最後獲取默認的dubbo.properties裏的路徑
                            path = Constants.DEFAULT_DUBBO_PROPERTIES;
                        }
                    }
                    //加載該路徑下的屬性值
                    PROPERTIES = ConfigUtils.loadProperties(path, false, true);
                }
            }
        }
        return PROPERTIES;
    }
    

loadProperties():

 

 public static Properties loadProperties(String fileName, boolean allowMultiFile, boolean optional) {
        Properties properties = new Properties();
        if (fileName.startsWith("/")) {
            try {
                FileInputStream input = new FileInputStream(fileName);
                try {
                    properties.load(input);
                } finally {
                    input.close();
                }
            } catch (Throwable e) {
                logger.warn("Failed to load " + fileName + " file from " + fileName + "(ingore this file): " + e.getMessage(), e);
            }
            return properties;
        }
        
        List<java.net.URL> list = new ArrayList<java.net.URL>();
        try {
            Enumeration<java.net.URL> urls = ClassHelper.getClassLoader().getResources(fileName);
            list = new ArrayList<java.net.URL>();
            while (urls.hasMoreElements()) {
                list.add(urls.nextElement());
            }
        } catch (Throwable t) {
            logger.warn("Fail to load " + fileName + " file: " + t.getMessage(), t);
        }
        
        if(list.size() == 0) {
            if (! optional) {
                logger.warn("No " + fileName + " found on the class path.");
            }
            return properties;
        }
        
        if(! allowMultiFile) {
            if (list.size() > 1) {
                String errMsg = String.format("only 1 %s file is expected, but %d dubbo.properties files found on class path: %s",
                        fileName, list.size(), list.toString());
                logger.warn(errMsg);
                // throw new IllegalStateException(errMsg); // see http://code.alibabatech.com/jira/browse/DUBBO-133
            }

            // fall back to use method getResourceAsStream
            try {
                properties.load(ClassHelper.getClassLoader().getResourceAsStream(fileName));
            } catch (Throwable e) {
                logger.warn("Failed to load " + fileName + " file from " + fileName + "(ingore this file): " + e.getMessage(), e);
            }
            return properties;
        }
        
        logger.info("load " + fileName + " properties file from " + list);
        
        for(java.net.URL url : list) {
            try {
                Properties p = new Properties();
                InputStream input = url.openStream();
                if (input != null) {
                    try {
                        p.load(input);
                        properties.putAll(p);
                    } finally {
                        try {
                            input.close();
                        } catch (Throwable t) {}
                    }
                }
            } catch (Throwable e) {
                logger.warn("Fail to load " + fileName + " file from " + url + "(ingore this file): " + e.getMessage(), e);
            }
        }
        
        return properties;
    }

此時properties的結果爲:

{dubbo.container=log4j,spring,

dubbo.service.loadbalance=roundrobin,

dubbo.registry.address=zookeeper://127.0.0.1:2181,

dubbo.application.owner=william,

dubbo.application.name=demo-provider,

dubbo.protocol.port=20880,

dubbo.protocol.name=dubbo}

4.添加dubbo.properties中指定的dubbo.container

   final List<Container> containers = new ArrayList<Container>();
            for (int i = 0; i < args.length; i ++) {
                containers.add(loader.getExtension(args[i]));
            }
 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, (T) clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (wrapperClasses != null && wrapperClasses.size() > 0) {
                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 + ")  could not be instantiated: " + t.getMessage(), t);
        }
    }

Class<?> clazz = getExtensionClasses().get(name);獲取對應key的class,

{spring=class com.alibaba.dubbo.container.spring.SpringContainer, jetty=class com.alibaba.dubbo.container.jetty.JettyContainer, log4j=class com.alibaba.dubbo.container.log4j.Log4jContainer}

測試:如果我在後面添加一個logback的容器,dubbo.container=log4j,spring, logback,此時運行報錯

原因找不到該爲logback的容器。

小結:

由於從消費者啓動容器開始梳理源碼,有些細節性的還沒涉及到,比如 擴展點的 Wrapper 類,@Adaptive的註解在dubbo中具體的作用,在後續的源碼解析流程中會細說。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章