天天看點

Dubbo源碼解析之:XML配置加載

      如果閱讀過前面文章的讀者可以知道,我們使用的版本是2.5.4。GitHub連結https://github.com/apache/dubbo。選擇tags的2.5.4下載下傳。後面發現阿裡将其重新開源并捐獻給Apache後,做了一些改變,後面我們将使用tags2.6.5版本作為我們講解版本。此文參考了https://juejin.im/post/5c1753b65188250850604ebe。

前言

在開始我們正式的講解之前,這裡先總覽dubbo-demo中生産者的xml配置檔案dubbo-demo-provider.xml,如果對xsd不了解的同學可以先看一下https://www.cnblogs.com/linjisong/p/3301096.html對xsd的介紹。

<?xml version="1.0" encoding="UTF-8"?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You 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.
  -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <!-- provider's application name, used for tracing dependency relationship -->
    <dubbo:application name="demo-provider"/>

    <!-- use multicast registry center to export service -->
    <!--<dubbo:registry address="multicast://224.5.6.7:1234"/>-->
    <dubbo:registry address="zookeeper://localhost:2181"/>

    <!-- use dubbo protocol to export service on port 20880 -->
    <dubbo:protocol name="dubbo" port="20880"/>

    <!-- service implementation, as same as regular local bean -->
    <bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/>

    <!-- declare the service interface to be exported -->
    <dubbo:service inter ref="demoService"/>

</beans>
           

不難猜想以 dubbo 開頭的 xml 标簽應該和 dubbo 密切相關。那麼這些标簽的用途是什麼?又是如何被識别的呢? 帶着這個疑問我們繼續往下面看。

1.Spring 自定義 XML 标簽解析

事實上Dubbo 中的自定義 XML 标簽實際上是依賴于 Spring 解析自定義标簽的功能實作的,我們結合 Spring 自定義 xml 标簽實作的相關内容來聊聊 Dubbo 是如何定義并加載這些自定義标簽的,當然這裡不會對Spring解析自定義XML标簽做過多解讀,僅介紹下實作相關功能需要的檔案,有需要的可以自己搜尋相關内容。

  • 1.1定義 xsd 檔案

    XSD(XML Schemas Definition) 即 XML 結構定義。我們通過 XSD 檔案不僅可以定義新的元素和屬性,同時也使用它對我們的 XML 檔案規範進行限制。 在 Dubbo 項目中可以找類似實作:dubbo.xsd。(dubbo捐獻給Apache以後對之前版本做了相容,是以這裡會有兩個xsd)

    Dubbo源碼解析之:XML配置加載
  • 1.2spring.schemas

    該配置檔案約定了自定義命名空間和 xsd 檔案之間的映射關系,用于 spring 容器感覺我們自定義的 xsd 檔案位置。

    http\://dubbo.apache.org/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd
    http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/compat/dubbo.xsd
               
    注:這裡同樣是為了相容之前版本的出現了兩行。
  • 1.3spring.handlers

    該配置檔案約定了自定義命名空間和 NamespaceHandler 類之間的映射關系。 NamespaceHandler 類用于注冊自定義标簽解析器。

    http\://dubbo.apache.org/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
    http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
               
    注:這裡同樣是為了相容之前版本的出現了兩行。
  • 1.4命名空間處理器

    命名空間處理器主要用來注冊 BeanDefinitionParser 解析器(dubbo中就對應上面 spring.handlers 檔案中的 DubboNamespaceHandler)

    public class DubboNamespaceHandler extends NamespaceHandlerSupport {
    }
               
  • 1.5BeanDefinitionParser 解析器

    實作 BeanDefinitionParser 接口中的 parse 方法,用于自定義标簽的解析(Dubbo 中對應 DubboBeanDefinitionParser 類)

    public class DubboBeanDefinitionParser implements BeanDefinitionParser {
        private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
        }
    }
               
    最後再給出 Spring 是如何從 XML 檔案中解析并加載 Bean 的。不了解沒關系可跳過
    Dubbo源碼解析之:XML配置加載

上圖言盡于 handler.parse() 方法,如果你仔細看了上文,對 parse() 應該是有印象的。沒錯,1.5小節我們介紹了 DubboBeanDefinitionParser 類。該類有個方法就叫 parse()。那麼這個 parse() 方法有什麼用? Spring 是如何感覺到我就要調用 DubboBeanDefinitionParser 類中的 parse() 方法的呢?我們帶着這兩個問題接着往下看。

2.Dubbo 解析自定義 XML 标簽

上圖最後一個類名為BeanDefinitionParserDelegate,那麼我們從這裡開始看起

BeanDefinitionParserDelegate.java
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
    // 擷取目前 element 的 namespaceURI
    // 比如 dubbo.xsd 中的為 http://dubbo.apache.org/schema/dubbo
    1、String namespaceUri = this.getNamespaceURI(ele);
    // 根據 URI 擷取對應的 NamespaceHandler
    2、NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
    if (handler == null) {
        this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
        return null;
    } else {
        3、return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }
}
           

這個方法幹了三件事

  1. 擷取 element 元素的 namespaceURI,并據此擷取對應的 NamespaceHandler 對象。Dubbo 自定義标簽(比如 Dubbo:provider) namespaceUri 的值為

    http://dubbo.apache.org/schema/dubbo

    ;
  2. 根據 step1 擷取到的 namespaceUri ,擷取對應的 NamespaceHandler 對象。這裡會調用 DefaultNamespaceHandlerResolver 類的 resolve() 方法,我們下面會分析;
  3. 調用 handler 的 parse 方法,我們自定以的 handler 會繼承 NamespaceHandlerSupport 類,是以這裡調用的其實是 NamespaceHandlerSupport 類的 parse() 方法,後文分析;

我們下面着重分析每個方法幹了什麼事情。

       上面parseCustomElement方法的第二步會調用resolve方法,我們來看看這個方法在哪裡定義的

public interface NamespaceHandlerResolver {

	/**
	 * Resolve the namespace URI and return the located {@link NamespaceHandler}
	 * implementation.
	 * @param namespaceUri the relevant namespace URI
	 * @return the located {@link NamespaceHandler} (may be {@code null})
	 */
	NamespaceHandler resolve(String namespaceUri);

}
           

 這是個接口,沒有具體實作,可以找到其實作類

public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {}
           

    具體實作的方法如下: 

DefaultNamespaceHandlerResolver.java
public NamespaceHandler resolve(String namespaceUri) {
    Map<String, Object> handlerMappings = this.getHandlerMappings();//在下面專門講解請先看下面
    // 以 namespaceUri 為 Key 擷取對應的 handlerOrClassName
    Object handlerOrClassName = handlerMappings.get(namespaceUri);
    if (handlerOrClassName == null) {
        return null;
    } else if (handlerOrClassName instanceof NamespaceHandler) {
        return (NamespaceHandler)handlerOrClassName;
    } else {
        // 如果不為空且不為 NamespaceHandler 的執行個體,轉換為 String 類型
        // DubboNamespaceHandler 執行的便是這段邏輯
        String className = (String)handlerOrClassName;
        try {
            Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
            // handlerClass 是否為 NamespaceHandler 的實作類,若不是則抛出異常
            if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
                throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
            } else {
                // 初始化 handlerClass
                NamespaceHandler namespaceHandler = (NamespaceHandler)BeanUtils.instantiateClass(handlerClass);
                // 執行 handlerClass類的 init() 方法
                namespaceHandler.init();
                handlerMappings.put(namespaceUri, namespaceHandler);
                return namespaceHandler;
            }
        } catch (ClassNotFoundException var7) {
            throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", var7);
        } catch (LinkageError var8) {
            throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", var8);
        }
    }
}
           

我們先看這個方法中調用了getHandlerMappings方法,在這個類裡可以找到這樣一個方法

/**
   * Load the specified NamespaceHandler mappings lazily.
*/
private Map<String, Object> getHandlerMappings() {
  if (this.handlerMappings == null) {
  //handlerMappings在這個類上方做了如下定義:private volatile Map<String, Object> handlerMappings;
   synchronized (this) {
    if (this.handlerMappings == null) {//用到了加鎖二次檢查
	   try {
        //Properties 繼承自Hashtable線程安全但是效率低
        //定義如下public class Properties extends Hashtable<Object,Object> 	
	Properties mappings =PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
        
    //實際上這裡的handlerMappingsLocation已經通過構造函數被指派為META-INF/spring.handlers,具體見源碼這樣就會用loadAllProperties讀取裡面的内容,還記得spring.handlers裡面是什麼嗎?傳回去看看
    //public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
    //private final String handlerMappingsLocation;
    //public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String                 
    //handlerMappingsLocation) {//省去非核心邏輯
    //	this.handlerMappingsLocation = handlerMappingsLocation;
    //}
    。。。省略
		Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
		CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
		this.handlerMappings = handlerMappings;
	省略。。。
	}
           

通過這個方法就拿到了一個map,map的key是

http\://dubbo.apache.org/schema/dubbo
value是com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
           

resolve() 方法用途是根據方法參數中的 namespaceUri 擷取對應的 NamespaceHandler 對象。這裡會先嘗試以 namespaceUri 為 key 去 handlerMappings 集合中取對象。 如果 handlerOrClassName 不為 null 且不為 NamespaceHandler 的執行個體。那麼嘗試将 handlerOrClassName 作為 className 并調用 BeanUtils.instantiateClass() 方法初始化一個 NamespaceHandler 執行個體。初始化後,調用其 init() 方法。

public class DubboNamespaceHandler extends NamespaceHandlerSupport {
 
    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }
 
    @Override
    public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
    }
 
}
           
public abstract class NamespaceHandlerSupport implements NamespaceHandler {

	/**
	 * Stores the {@link BeanDefinitionParser} implementations keyed by the
	 * local name of the {@link Element Elements} they handle.
	 */
	private final Map<String, BeanDefinitionParser> parsers =
			new HashMap<String, BeanDefinitionParser>();
	/**
	 * Subclasses can call this to register the supplied {@link BeanDefinitionParser} to
	 * handle the specified element. The element name is the local (non-namespace qualified)
	 * name.
	 */
	protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
		this.parsers.put(elementName, parser);
	}

}
           

DubboNamespaceHandler 類中的 init() 方法幹的事情特别簡單,就是建立 DubboBeanDefinitionParser 對象并将其放入 NamespaceHandlerSupport 類中建立的的 parsers 集合中。(細心的讀者可以看到init後面的 RegistryConfig.class裡的字段和xsd檔案裡的屬性基本是一緻的)

parseCustomElement方法的第三步會調用parse方法,具體會調用 NamespaceHandlerSupport 類的 parse() 方法

public BeanDefinition parse(Element element, ParserContext parserContext) {
    return this.findParserForElement(element, parserContext).parse(element, parserContext);
}
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
    String localName = parserContext.getDelegate().getLocalName(element);
    BeanDefinitionParser parser = (BeanDefinitionParser)this.parsers.get(localName);
    if (parser == null) {
        parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
    }

    return parser;
}
           

看到這裡大家有沒有一絲豁然開朗的感覺?之前的 resolve() 方法實際上就是根據目前 element 的 namespaceURI 擷取對應的 NamespaceHandler 對象(對于 Dubbo 來說是 DubboNamespaceHandler), 然後調用 DubboNamespaceHandler 中的 init() 方法建立 DubboBeanDefinitionParser 對象并注冊到 NamespaceHandlerSupport 類的 parsers 集合中。 然後 parser 方法會根據目前 element 對象從 parsers 集合中擷取合适的 BeanDefinitionParser 對象。對于 Dubbo 元素來說,實際上最後執行的是 DubboBeanDefinitionParser 的 parse() 方法。

private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClass(beanClass);
        beanDefinition.setLazyInit(false);
        String id = element.getAttribute("id");
        // DubboBeanDefinitionParser 構造方法中有對 required 值進行初始化;
        // DubboNamespaceHandler 類中的 init 方法會建立并注冊 DubboBeanDefinitionParser 類
        if ((id == null || id.length() == 0) && required) {
            String generatedBeanName = element.getAttribute("name");
            if (generatedBeanName == null || generatedBeanName.length() == 0) {
                if (ProtocolConfig.class.equals(beanClass)) {
                    generatedBeanName = "dubbo";
                } else {
                    // name 屬性為空且不為 ProtocolConfig 類型,取 interface 值
                    generatedBeanName = element.getAttribute("interface");
                }
            }
            if (generatedBeanName == null || generatedBeanName.length() == 0) {
                // 擷取 beanClass 的全限定類名
                generatedBeanName = beanClass.getName();
            }
            id = generatedBeanName;
            int counter = 2;
            while (parserContext.getRegistry().containsBeanDefinition(id)) {
                id = generatedBeanName + (counter++);
            }
        }
        if (id != null && id.length() > 0) {
            if (parserContext.getRegistry().containsBeanDefinition(id)) {
                throw new IllegalStateException("Duplicate spring bean id " + id);
            }
            // 注冊 beanDefinition
            parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
            // 為 beanDefinition 添加 id 屬性
            beanDefinition.getPropertyValues().addPropertyValue("id", id);
        }
        
        // 如果目前 beanClass 類型為 ProtocolConfig
        // 周遊已經注冊過的 bean 對象,如果 bean 對象含有 protocol 屬性
        // protocol 屬性值為 ProtocolConfig 執行個體且 name 和目前 id 值一緻,為目前 beanClass 對象添加 protocl 屬性
        if (ProtocolConfig.class.equals(beanClass)) {
            for (String name : parserContext.getRegistry().getBeanDefinitionNames()) {
                BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name);
                PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol");
                if (property != null) {
                    Object value = property.getValue();
                    if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) {
                        definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
                    }
                }
            }
        } else if (ServiceBean.class.equals(beanClass)) {
            // 如果目前元素包含 class 屬性,調用 ReflectUtils.forName() 方法加載類對象
            // 調用 parseProperties 解析其他屬性設定到 classDefinition 對象中
            // 最後設定 beanDefinition 的 ref 屬性為 BeanDefinitionHolder 包裝類
            String className = element.getAttribute("class");
            if (className != null && className.length() > 0) {
                RootBeanDefinition classDefinition = new RootBeanDefinition();
                classDefinition.setBeanClass(ReflectUtils.forName(className));
                classDefinition.setLazyInit(false);
                parseProperties(element.getChildNodes(), classDefinition);
                beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
            }
        } else if (ProviderConfig.class.equals(beanClass)) {
            parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);
        } else if (ConsumerConfig.class.equals(beanClass)) {
            parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);
        }
        Set<String> props = new HashSet<String>();
        ManagedMap parameters = null;
        for (Method setter : beanClass.getMethods()) {
            String name = setter.getName();
            if (name.length() > 3 && name.startsWith("set")
                    && Modifier.isPublic(setter.getModifiers())
                    && setter.getParameterTypes().length == 1) {
                Class<?> type = setter.getParameterTypes()[0];
                String propertyName = name.substring(3, 4).toLowerCase() + name.substring(4);
                String property = StringUtils.camelToSplitName(propertyName, "-");
                props.add(property);
                Method getter = null;
                try {
                    getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);
                } catch (NoSuchMethodException e) {
                    try {
                        getter = beanClass.getMethod("is" + name.substring(3), new Class<?>[0]);
                    } catch (NoSuchMethodException e2) {
                    }
                }
                if (getter == null
                        || !Modifier.isPublic(getter.getModifiers())
                        || !type.equals(getter.getReturnType())) {
                    continue;
                }
                if ("parameters".equals(property)) {
                    parameters = parseParameters(element.getChildNodes(), beanDefinition);
                } else if ("methods".equals(property)) {
                    parseMethods(id, element.getChildNodes(), beanDefinition, parserContext);
                } else if ("arguments".equals(property)) {
                    parseArguments(id, element.getChildNodes(), beanDefinition, parserContext);
                } else {
                    String value = element.getAttribute(property);
                    if (value != null) {
                        value = value.trim();
                        if (value.length() > 0) {
                        // 如果屬性為 registry,且 registry 屬性的值為"N/A",辨別不會注冊到任何注冊中心
                        // 建立 RegistryConfig 并将其設定為 beanDefinition 的 registry 屬性
                            if ("registry".equals(property) && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) {
                                RegistryConfig registryConfig = new RegistryConfig();
                                registryConfig.setAddress(RegistryConfig.NO_AVAILABLE);
                                beanDefinition.getPropertyValues().addPropertyValue(property, registryConfig);
                            } else if ("registry".equals(property) && value.indexOf(',') != -1) {
                                // 多注冊中心解析
                                parseMultiRef("registries", value, beanDefinition, parserContext);
                            } else if ("provider".equals(property) && value.indexOf(',') != -1) {
                                parseMultiRef("providers", value, beanDefinition, parserContext);
                            } else if ("protocol".equals(property) && value.indexOf(',') != -1) {
                                // 多協定
                                parseMultiRef("protocols", value, beanDefinition, parserContext);
                            } else {
                                Object reference;
                                if (isPrimitive(type)) {
                                    // type 為方法參數,type 類型是否為基本類型
                                    if ("async".equals(property) && "false".equals(value)
                                            || "timeout".equals(property) && "0".equals(value)
                                            || "delay".equals(property) && "0".equals(value)
                                            || "version".equals(property) && "0.0.0".equals(value)
                                            || "stat".equals(property) && "-1".equals(value)
                                            || "reliable".equals(property) && "false".equals(value)) {
                                        // 新老版本 xsd 相容性處理
                                        // backward compatibility for the default value in old version's xsd
                                        value = null;
                                    }
                                    reference = value;
                                } else if ("protocol".equals(property)
                                        && ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(value)
                                        && (!parserContext.getRegistry().containsBeanDefinition(value)
                                        || !ProtocolConfig.class.getName().equals(parserContext.getRegistry().getBeanDefinition(value).getBeanClassName()))) {
                                    // 如果 protocol 屬性值有對應的擴充實作,而且沒有被注冊到 spring 系統資料庫中
                                    // 或者 spring 系統資料庫中對應的 bean 的類型不為 ProtocolConfig.class
                                    if ("dubbo:provider".equals(element.getTagName())) {
                                        logger.warn("Recommended replace <dubbo:provider protocol=\"" + value + "\" ... /> to <dubbo:protocol name=\"" + value + "\" ... />");
                                    }
                                    // backward compatibility
                                    ProtocolConfig protocol = new ProtocolConfig();
                                    protocol.setName(value);
                                    reference = protocol;
                                } else if ("onreturn".equals(property)) {
                                    int index = value.lastIndexOf(".");
                                    String returnRef = value.substring(0, index);
                                    String returnMethod = value.substring(index + 1);
                                    reference = new RuntimeBeanReference(returnRef);
                                    beanDefinition.getPropertyValues().addPropertyValue("onreturnMethod", returnMethod);
                                } else if ("onthrow".equals(property)) {
                                    int index = value.lastIndexOf(".");
                                    String throwRef = value.substring(0, index);
                                    String throwMethod = value.substring(index + 1);
                                    reference = new RuntimeBeanReference(throwRef);
                                    beanDefinition.getPropertyValues().addPropertyValue("onthrowMethod", throwMethod);
                                } else if ("oninvoke".equals(property)) {
                                    int index = value.lastIndexOf(".");
                                    String invokeRef = value.substring(0, index);
                                    String invokeRefMethod = value.substring(index + 1);
                                    reference = new RuntimeBeanReference(invokeRef);
                                    beanDefinition.getPropertyValues().addPropertyValue("oninvokeMethod", invokeRefMethod);
                                } else {
                                    // 如果 ref 屬性值已經被注冊到 spring 系統資料庫中
                                    if ("ref".equals(property) && parserContext.getRegistry().containsBeanDefinition(value)) {
                                        BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value);
                                        // 非單例抛出異常
                                        if (!refBean.isSingleton()) {
                                            throw new IllegalStateException("The exported service ref " + value + " must be singleton! Please set the " + value + " bean scope to singleton, eg: <bean id=\"" + value + "\" scope=\"singleton\" ...>");
                                        }
                                    }
                                    reference = new RuntimeBeanReference(value);
                                }
                                beanDefinition.getPropertyValues().addPropertyValue(propertyName, reference);
                            }
                        }
                    }
                }
            }
        }
        NamedNodeMap attributes = element.getAttributes();
        int len = attributes.getLength();
        for (int i = 0; i < len; i++) {
            Node node = attributes.item(i);
            String name = node.getLocalName();
            if (!props.contains(name)) {
                if (parameters == null) {
                    parameters = new ManagedMap();
                }
                String value = node.getNodeValue();
                parameters.put(name, new TypedStringValue(value, String.class));
            }
        }
        if (parameters != null) {
            beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);
        }
        return beanDefinition;
    }
           

上面這一大段關于配置的解析的代碼需要大家自己結合實際的代碼進行調試才能更好的了解。确實不是很好了解,可以選擇跳過,知道大緻流程就行,最後放張Dubbo解析的時序圖

Dubbo源碼解析之:XML配置加載

繼續閱讀