這篇文章是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)看下有哪些:
可以看見(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