??spring 為第三方預(yù)留了集成的空間欢瞪,當(dāng)spring遇到非自己的命名空間時(shí)活烙,會(huì)去META-INF/spring.handlers文件中尋找此命名空間的處理器,解析xml配置文件時(shí)遣鼓,遇到此命名空間的標(biāo)簽啸盏,調(diào)用對(duì)應(yīng)命名空間標(biāo)簽處理器,解析此標(biāo)簽譬正。那么dubbo怎么做的呢宫补?在dubbo源碼的META-INF目錄下,我們看到有spring.handler文件曾我,打開(kāi)此文件內(nèi)容為:
http://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
表明dubbo標(biāo)簽的文件處理器為DubboNamespaceHandler粉怕,找到這個(gè)類我們可以發(fā)現(xiàn)所有標(biāo)簽的解析處理邏輯。
??dubbo命名空間下所有的標(biāo)簽都是調(diào)用DubboBeanDefinitionParser進(jìn)行解析抒巢,此方法有兩個(gè)參數(shù)贫贝,一個(gè)是配置類,此配置類會(huì)注冊(cè)到spring IoC容器中蛉谜,另一個(gè)是此bean在IoC容器中是否需要ID標(biāo)識(shí)稚晚。
public BeanDefinitionparse(Element element, ParserContext parserContext) {
return parse(element, parserContext, beanClass, required);
}
private static BeanDefinitionparse(Element element, ParserContext parserContext, Class beanClass, boolean required) {
RootBeanDefinition beanDefinition =new RootBeanDefinition();//new一個(gè)beanDefinition
beanDefinition.setBeanClass(beanClass);
beanDefinition.setLazyInit(false);//立即初始化
String id = element.getAttribute("id");
if ((id ==null || id.length() ==0) && required) {//設(shè)置bean id
beanDefinition.getPropertyValues().addPropertyValue("id", id);//設(shè)置bean id
}
if (ProtocolConfig.class.equals(beanClass)) {//此處是為服務(wù)設(shè)置協(xié)議
}else if (ServiceBean.class.equals(beanClass)){//若class屬性不為空,ref屬性設(shè)置為class
}else if (ProviderConfig.class.equals(beanClass)) {//遞歸解析下級(jí)標(biāo)簽
}else if (ConsumerConfig.class.equals(beanClass)) {
}
for (Method setter : beanClass.getMethods()) {
String name = setter.getName();
if (name.length() >3 && name.startsWith("set")
&& Modifier.isPublic(setter.getModifiers())
&& setter.getParameterTypes().length ==1) { //取出標(biāo)簽屬性的值型诚,設(shè)置到bean對(duì)象中
..........................................................................
beanDefinition.getPropertyValues().addPropertyValue(property, reference);
..........................................................................
}
return beanDefinition;
}
DubboBeanDefinitionParser中的parse方法客燕,首先new一個(gè)RootBeanDefinition,然后設(shè)置beanclass值狰贯,將bean的延遲初始化設(shè)為false也搓,接著如下圖所示,如果id為空涵紊,并且id是必須的傍妒,則設(shè)置id值,具體為:
1)先從元素節(jié)點(diǎn)看是否能獲取到name屬性
2)如果獲取不到摸柄,且class為protocolConfig颤练,則將id設(shè)置成dubbo,不是protocolconfig,取接口值
3)如果還是獲取不到驱负,取class的名稱
//將剛才定義的beandefinition注冊(cè)到spring IoC容器中嗦玖,并設(shè)置id值
if (id != null && id.length() > 0) {
if (parserContext.getRegistry().containsBeanDefinition(id)) {
throw new IllegalStateException("Duplicate spring bean id " + id);
}
parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
beanDefinition.getPropertyValues().addPropertyValue("id", id);
}
?接下來(lái)對(duì)ProtocolConfig患雇、ServiceBean、ProviderConfig踏揣、ConsumerConfig做特殊處理庆亡。
1)ProtocolConfig:獲取IoC容器里所有的BeanDefinition,然后看這個(gè)bean是否有名稱為protocol的屬性定義捞稿,若有又谋,且協(xié)議為當(dāng)前所解析的協(xié)議,則設(shè)置此bean protocol屬性值為當(dāng)前協(xié)議(此處設(shè)置為動(dòng)態(tài)引用娱局,初始化的時(shí)候才設(shè)置值彰亥,具體可參考spring bean機(jī)制)
2)ServiceBean :如果是<dubbo:service>標(biāo)簽class屬性不為空,將此class設(shè)置成為一個(gè)beandefinition衰齐,并且作為屬性ref的值任斋。(可見(jiàn)ref class兩個(gè)屬性二選1)
3)ProviderConfig、ConsumerConfig:逐層解析下級(jí)標(biāo)簽耻涛,每個(gè)下級(jí)標(biāo)簽解析成一個(gè)beanDefinition废酷,并將provider、consumer作為下級(jí)標(biāo)簽的一個(gè)屬性抹缕,值為動(dòng)態(tài)引用的bean(serviceBean澈蟆、ReferenceBean)
接下來(lái)就是遍歷beanClass中所有的方法(set開(kāi)頭,public修飾卓研,有參數(shù))趴俘,設(shè)置bean中屬性值
for (Method setter : beanClass.getMethods()) {
String name = setter.getName();
if (name.length() >3 && name.startsWith("set")
&& Modifier.isPublic(setter.getModifiers())
&& setter.getParameterTypes().length ==1) {
..............................................................
beanDefinition.getPropertyValues().addPropertyValue(property, reference);
..............................................................
}
?這樣所有配置標(biāo)簽就被解析成beanDefinition交給spring管理,待所有標(biāo)簽都解析完成之后奏赘,spring就會(huì)初始化Bean寥闪,由于serviceBean、ReferenceBean比較特殊磨淌,實(shí)現(xiàn)了一系列接口疲憋,當(dāng)spring在初始化這些這兩個(gè)bean的時(shí)候,會(huì)執(zhí)行相對(duì)應(yīng)的方法梁只,具體如下:
public class ServiceBeanextends ServiceConfig implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener, BeanNameAware{
public void afterPropertiesSet()//完成發(fā)布服務(wù)所需配置的初始化
public void onApplicationEvent(ApplicationEvent event)//完成服務(wù)的暴露
}
public class ReferenceBeanextends ReferenceConfig implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean{
public void afterPropertiesSet()//完成引用服務(wù)所需配置的初始化
public ObjectgetObject()throws Exception {
return get(); //完成服務(wù)的引用
}
}
總結(jié):由于spring的應(yīng)用廣泛缚柳,包括dubbo在內(nèi)的rpc框架都利用spring IoC容器完成服務(wù)的啟動(dòng)。具體如下:將發(fā)布和引用服務(wù)所需的配置定義成bean--->讀取配置文件敛纲,完成對(duì)bean的填充--->相關(guān)的bean實(shí)現(xiàn)接口--->spring 容器初始化bean的時(shí)候喂击,調(diào)用相應(yīng)的方法完成服務(wù)的發(fā)布和引用剂癌。
一篇好文章:https://nobodyiam.com/2017/02/26/several-ways-to-extend-spring/