研究dubbo就先從自己最感興趣的開始吧
一、構建dubbo源碼環境
① dubbo源碼在github中,右上角fork一個分支到自己的github,然后直接git clone拉代碼到本地。(用手機熱點拉代碼有驚喜)
② 改阿里雲鏡像,maven的settings.xml
<localRepository>D:/mavenRepository</localRepository> <mirrors> <mirror> <id>nexus-aliyun</id> <mirrorOf>*</mirrorOf> <name>Nexus aliyun</name> <url>http://maven.aliyun.com/nexus/content/groups/public</url> </mirror> </mirrors>
③ 導入maven項目到idea中,等待jar依賴下載。
前期學習目標:主要是過一遍源碼流程,網上看的架構圖都太抽象了,看完源碼之后,再去理解。
二、配置解析
官方已經推薦注解形式配置dubbo的服務了,由於工作時用的都是xml配置文件配置dubbo的服務,直接從源碼查找注解解析對於我來說有點無從下手的感覺。還是從xml配置文件入手順手一點。dubbo-demo>dubbo-demo-xml>dubbo-demo-xml-provider下的一個提供xml配置例子。
<?xml version="1.0" encoding="UTF-8"?> <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"> <dubbo:application metadata-type="remote" name="demo-provider"/> <dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/> <dubbo:registry address="zookeeper://127.0.0.1:2181"/> <dubbo:protocol name="dubbo"/> <bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/> <dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService"/> </beans>
通過spring.handlers文件找到dubbo命名空間的解析類
/* dubbo-config/dubbo-config-spring/src/main/resources/META-INF/spring.handlers */ 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
Apache和阿里巴巴版本的命名空間進行了統一,指向的是一個命名空間解析類
/* org.apache.dubbo.config.spring.schema.DubboNamespaceHandler#init */ 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("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true)); registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true)); registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true)); registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.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()); }
前面spring——scan()中是根據注解解析成BeanDefinition,還沒有研究過xml解析過程,這里走一遍吧,看看什么時候會調用上面init方法
/* AbstractApplicationContex.refresh()--> AbstractApplicationContext.obtainFreshBeanFactory()--> AbstractRefreshableApplicationContext.refreshBeanFactory()--> AbstractXmlApplicationContext.loadBeanDefinitions()--> AbstractXmlApplicationContext.loadBeanDefinitions()(重載方法)--> AbstractBeanDefinitionReader.loadBeanDefinitions()--> XmlBeanDefinitionReader.loadBeanDefinitions()--> XmlBeanDefinitionReader.loadBeanDefinitions()(重載方法)--> XmlBeanDefinitionReader.doLoadBeanDefinitions()--> XmlBeanDefinitionReader.registerBeanDefinitions()--> DefaultBeanDefinitionDocumentReader.registerBeanDefinitions()--> DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions()--> DefaultBeanDefinitionDocumentReader.parseBeanDefinitions()--> BeanDefinitionParserDelegate.parseCustomElement()--> BeanDefinitionParserDelegate.parseCustomElement()(重載方法)*/ public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { //解析根節點時,獲取xml根節點的xmlns屬性,然后緩存起來 //dubbo的是http://dubbo.apache.org/schema/dubbo String namespaceUri = getNamespaceURI(ele); //初始化NamespaceHandler //① 獲取META-INF/spring.handlers文件得到指定NamespaceHandler的className // dubbo是org.apache.dubbo.config.spring.schema.DubboNamespaceHandler //② 反射初始化並調用NamespaceHandler.init()注冊所有的節點解析器 //③ 返回namespaceHandler NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } //namespaceHandle根據子節點名找到解析類,解析標簽生成BeanDefinition return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }
DubboNamespaceHandler.parse():標簽節點解析
/* org.apache.dubbo.config.spring.schema.DubboNamespaceHandler#parse */ public BeanDefinition parse(Element element, ParserContext parserContext) { //獲取BeanFactory BeanDefinitionRegistry registry = parserContext.getRegistry(); //注解注解相關的解析器 registerAnnotationConfigProcessors(registry); //注冊兩個監聽器 //DubboLifecycleComponentApplicationListener //DubboBootstrapApplicationListener(處理服務注冊邏輯,IOC初始化refresh的最后一步finishrefresh()) registerApplicationListeners(registry); //標簽節點解析為BeanDefinition BeanDefinition beanDefinition = super.parse(element, parserContext); setSource(beanDefinition); return beanDefinition; }
總結:springIOC初始化主要完成兩個任務
1、提前初始化注解解析器,注冊兩個監聽器,一個用於生命周期監聽,一個用於serviceBean注冊到注冊表
2、將標簽解析成bean,並生成實例
3、注冊到注冊表(DubboBootstrapApplicationListener.onApplicationEvent())
補充:
1、上面所有Config都繼承了AbstractConfig,需要注意的是AbstractConfig的一個方法被@PostConstruct注解。
@PostConstruct注解:Bean初始化(DI)后,BeanPostProcessor處理器會以反射的方式,使bean實例執行此方法。
這里addIntoConfigManager()方法作用:將上面所有的config信息,都放入configManager中。
@PostConstruct public void addIntoConfigManager() { ApplicationModel.getConfigManager().addConfig(this); }