Dubbo2.7源碼分析-Dubbo是如何整合spring framework的

這篇文章是Dubbo源碼分析的開(kāi)端惩猫,與其說(shuō)這篇文章是Dubbo源碼分析,不如是spring源碼分析蚜点,因?yàn)榇蟛糠侄际窃诜治鰏pring如何解析xml配置文件的轧房,為了與后面的Dubbo源碼分析保持一致,姑且這樣命名了
使用Dubbo框架開(kāi)發(fā)分布式服務(wù)時(shí)绍绘,一般使用spring進(jìn)行管理奶镶,在spring的配置文件中進(jìn)行配置,例如服務(wù)提供者Provider端配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
    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">
 
    <!-- 提供方應(yīng)用信息陪拘,用于計(jì)算依賴(lài)關(guān)系 -->
    <dubbo:application name="hello-world-app"  />
 
    <!-- 使用multicast廣播注冊(cè)中心暴露服務(wù)地址 -->
    <dubbo:registry address="multicast://224.5.6.7:1234" />
 
    <!-- 用dubbo協(xié)議在20880端口暴露服務(wù) -->
    <dubbo:protocol name="dubbo" port="20880" />
 
    <!-- 聲明需要暴露的服務(wù)接口 -->
    <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" />
 
    <!-- 和本地bean一樣實(shí)現(xiàn)服務(wù) -->
    <bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" />
</beans>

對(duì)于<bean/>標(biāo)簽我們都熟悉了厂镇,這是spring提供給使用者實(shí)例化PoJo類(lèi)的。那么dubbo定義的標(biāo)簽spring是如何識(shí)別的呢左刽?
其實(shí)spring-beans jar包中提供了一個(gè)口捺信,那就是NamespaceHandler,它的定義如下:

(省略描述....)
public interface NamespaceHandler {
    void init();

    BeanDefinition parse(Element element, ParserContext parserContext);

    BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);

}

可以看出其中有一個(gè)parse方法欠痴,它的作用就是用于解析Xml文檔中的節(jié)點(diǎn)迄靠。這個(gè)接口有一個(gè)抽象的實(shí)現(xiàn)類(lèi)NamespaceHandlerSupport,具體的標(biāo)簽解析器都繼承這個(gè)抽象類(lèi)喇辽,我們來(lái)看下有哪些:


NamespaceHandler

可以看見(jiàn)我們熟悉的Aop還有Dubbo NamespaceHandler.那Spring是如何知道要使用Dubbo定義的handler來(lái)解析自定義的標(biāo)簽?zāi)卣浦浚克麄兊慕Y(jié)合點(diǎn)就在一個(gè)配置文件,spring留了一個(gè)配置文件茵臭,只要我們配置了它疫诽,spring就可以找到。這個(gè)配置文件名字叫做spring.handlers,spring在解析xml文件時(shí)旦委,會(huì)去加載spring.handlers配置文件奇徒,然后尋找能夠解析自定義標(biāo)簽的handler。
spring.handlers在Dubbo中是怎樣的內(nèi)容呢缨硝,我們一起來(lái)看下:
路徑在:dubbo-config/dubbon-config-spring/META-INF/spring.handlers

內(nèi)容如下:

http://dubbo.apache.org/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
http://code.alibabatech.com/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler

我們來(lái)看一下spring是如何找到這個(gè)文件并生成對(duì)應(yīng)標(biāo)簽namespaceHandler的:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"application.xml"});
我們使用spring框架摩钙,啟動(dòng)的時(shí)候都會(huì)寫(xiě)這么一句話,加載xml配置文件.當(dāng)執(zhí)行到AbstractXmlApplicationContext類(lèi)的loadBeanDefinitions方法時(shí)查辩,會(huì)創(chuàng)建一個(gè)XmlBeanDefinitionReader對(duì)象胖笛,讀取讀取并解析xml.

    /**
     * Loads the bean definitions via an XmlBeanDefinitionReader.
     * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
     * @see #initBeanDefinitionReader
     * @see #loadBeanDefinitions
     */
    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Configure the bean definition reader with this context's
        // resource loading environment.
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // Allow a subclass to provide custom initialization of the reader,
        // then proceed with actually loading the bean definitions.
        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    }

最后一句話loadBeanDefinitions(beanDefinitionReader),是使用XmlBeanDefinitionReader將xml解析成BeanDefinition.
接下來(lái)調(diào)用XmlBeanDefinitionReader的registerBeanDefinitions方法創(chuàng)建BeanDefinitionDocumentReader對(duì)象,這個(gè)對(duì)象才是真正解析XML的對(duì)象宜岛。

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();
        //創(chuàng)建ReaderContext长踊,并調(diào)用documentReader的registerBeanDefinitions方法
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

大家再來(lái)看下createReaderContext方法

    /**
     * Create the {@link XmlReaderContext} to pass over to the document reader.
     */
    public XmlReaderContext createReaderContext(Resource resource) {
        return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
                this.sourceExtractor, this, getNamespaceHandlerResolver());
    }

終于出現(xiàn)了NamespaceHandler,那就是getNamespaceHandlerResolver()方法萍倡,再來(lái)看下這個(gè)方法

    public NamespaceHandlerResolver getNamespaceHandlerResolver() {
        if (this.namespaceHandlerResolver == null) {
            this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
        }
        return this.namespaceHandlerResolver;
    }
    
    protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
        return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
    }
    
    public DefaultNamespaceHandlerResolver(ClassLoader classLoader) {
        this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
    }

//最終會(huì)調(diào)用這個(gè)構(gòu)造器實(shí)例化DefaultNamespaceHandlerResolver類(lèi)身弊,此時(shí)handlerMappingsLocation成員變量的值為META-INF/spring.handlers了
    public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) {
        Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
        this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
        this.handlerMappingsLocation = handlerMappingsLocation;
    }

    /**
     * The location to look for the mapping files. Can be present in multiple JAR files.
     */
    public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";

//讀取META-INF/spring.handlers配置文件的內(nèi)容
    private Map<String, Object> getHandlerMappings() {
        if (this.handlerMappings == null) {
            synchronized (this) {
                if (this.handlerMappings == null) {
                    try {
                        Properties mappings =
                                PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
                        if (logger.isDebugEnabled()) {
                            logger.debug("Loaded NamespaceHandler mappings: " + mappings);
                        }
                        Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
                        CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
                        this.handlerMappings = handlerMappings;
                    }
                    catch (IOException ex) {
                        throw new IllegalStateException(
                                "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
                    }
                }
            }
        }
        return this.handlerMappings;
    }

會(huì)創(chuàng)建一個(gè)默認(rèn)的DefaultNamespaceHandlerResolver對(duì)象,其中有一個(gè)變量DEFAULT_HANDLER_MAPPINGS_LOCATION,其值是META-INF/spring.handlers,此時(shí)可以看到spring會(huì)去加載spring.hadlers中的內(nèi)容。
那加載完后阱佛,會(huì)在什么地方使用呢帖汞?
我們來(lái)看一下DefaultBeanDefinitionDocumentReader類(lèi)中的parseBeanDefinitions方法

    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    //判斷標(biāo)簽是標(biāo)準(zhǔn)的標(biāo)準(zhǔn)還是自定義的,判斷的依據(jù)就是標(biāo)準(zhǔn)的namespace是否是http://www.springframework.org/schema/beans
                    //如果是凑术,則是標(biāo)準(zhǔn)的標(biāo)簽翩蘸,如果不是則不是標(biāo)準(zhǔn)的標(biāo)簽
                    if (delegate.isDefaultNamespace(ele)) {
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }

接下來(lái)看一下BeanDefinitionParserDelegate類(lèi)的parseCustomElement方法,BeanDefinitionParserDelegate對(duì)象是在DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions方法中生成的
淮逊,它的作用是用來(lái)解析標(biāo)簽的催首。

    public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
        //根據(jù)標(biāo)簽得到標(biāo)簽的namespace
        //dubbo自定義的標(biāo)簽得到的namespace是http://dubbo.apache.org/schema/dubbo
        String namespaceUri = getNamespaceURI(ele);
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler == null) {
            error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
            return null;
        }
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }

接下來(lái)看下DefaultBeanDefinitionDocumentReader的resolve方法

    @Override
    public NamespaceHandler resolve(String namespaceUri) {
                //得到加載的內(nèi)容
        Map<String, Object> handlerMappings = getHandlerMappings();
               //根據(jù)標(biāo)簽的namspace得到handler類(lèi)名
              //dubbo的namespace為http://dubbo.apache.org/schema/dubbo
        Object handlerOrClassName = handlerMappings.get(namespaceUri);
        if (handlerOrClassName == null) {
            return null;
        }
        else if (handlerOrClassName instanceof NamespaceHandler) {
            return (NamespaceHandler) handlerOrClassName;
        }
        else {
            String className = (String) handlerOrClassName;
            try {
                Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
                if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
                    throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
                            "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
                }
//實(shí)例化namespaceHandler
                NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
                namespaceHandler.init();
                handlerMappings.put(namespaceUri, namespaceHandler);
                return namespaceHandler;
            }
            catch (ClassNotFoundException ex) {
                throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
                        namespaceUri + "] not found", ex);
            }
            catch (LinkageError err) {
                throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
                        namespaceUri + "]: problem with handler class file or dependent class", err);
            }
        }
    }

得到DubboNamespaceHandler實(shí)例后,調(diào)用其init()方法

    @Override
    public void init() {
        //將application壮莹、module等名稱(chēng)做為key注冊(cè)到解析器Map中
       //這些key名稱(chēng)正是dubbo自定義標(biāo)簽的localName,例如<dubbo:application />標(biāo)簽的LocalName是application
        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());
    }

可以看到所有的key(除了annotation)對(duì)應(yīng)的標(biāo)簽解析器都是DubboBeanDefinitionParser實(shí)例對(duì)象翅帜,至于DubboBeanDefinitionParser是如何解析標(biāo)簽的,這里就不做分析了命满,如果想要了解,可以看看他的源碼绣版。

如果不做特別說(shuō)明,此篇往后所有的Dubbo源碼分析都是基于Dubbo2.7.0

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末胶台,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子杂抽,更是在濱河造成了極大的恐慌诈唬,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缩麸,死亡現(xiàn)場(chǎng)離奇詭異铸磅,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)杭朱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)阅仔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人弧械,你說(shuō)我怎么就攤上這事八酒。” “怎么了刃唐?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵羞迷,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我画饥,道長(zhǎng)衔瓮,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任抖甘,我火速辦了婚禮热鞍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己碍现,他們只是感情好幅疼,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著昼接,像睡著了一般爽篷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上慢睡,一...
    開(kāi)封第一講書(shū)人閱讀 49,764評(píng)論 1 290
  • 那天逐工,我揣著相機(jī)與錄音,去河邊找鬼漂辐。 笑死泪喊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的髓涯。 我是一名探鬼主播袒啼,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼纬纪!你這毒婦竟也來(lái)了蚓再?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤包各,失蹤者是張志新(化名)和其女友劉穎摘仅,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體问畅,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡娃属,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了护姆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片矾端。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖签则,靈堂內(nèi)的尸體忽然破棺而出须床,到底是詐尸還是另有隱情,我是刑警寧澤渐裂,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布豺旬,位于F島的核電站,受9級(jí)特大地震影響柒凉,放射性物質(zhì)發(fā)生泄漏族阅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一膝捞、第九天 我趴在偏房一處隱蔽的房頂上張望坦刀。 院中可真熱鬧,春花似錦、人聲如沸鲤遥。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)盖奈。三九已至混坞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間钢坦,已是汗流浹背究孕。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留爹凹,地道東北人厨诸。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像禾酱,于是被迫代替她去往敵國(guó)和親微酬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容