这篇文章是Dubbo源码分析的开端,与其说这篇文章是Dubbo源码分析,不如是spring源码分析,因为大部分都是在分析spring如何解析xml配置文件的,为了与后面的Dubbo源码分析保持一致,姑且这样命名了。
使用Dubbo框架开发分布式服务时,一般使用spring进行管理,在spring的配置文件中进行配置,例如服务提供者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">
<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="hello-world-app" />
<!-- 使用multicast广播注册中心暴露服务地址 -->
<dubbo:registry address="multicast://224.5.6.7:1234" />
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" />
<!-- 和本地bean一样实现服务 -->
<bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" />
</beans>
对于
其实spring-beans jar包中提供了一个口,那就是NamespaceHandler,它的定义如下:@H_502_10@
(省略描述....)
public interface NamespaceHandler {
void init();
BeanDefinition parse(Element element,ParserContext parserContext);
BeanDefinitionHolder decorate(Node source,BeanDefinitionHolder definition,ParserContext parserContext);
}
可以看出其中有一个parse方法,它的作用就是用于解析Xml文档中的节点。这个接口有一个抽象的实现类NamespaceHandlerSupport,具体的标签解析器都继承这个抽象类,我们来看下有哪些:
可以看见我们熟悉的Aop还有Dubbo NamespaceHandler.那Spring是如何知道要使用Dubbo定义的handler来解析自定义的标签呢?他们的结合点就在一个配置文件,spring留了一个配置文件,只要我们配置了它,spring就可以找到。这个配置文件名字叫做spring.handlers,spring在解析xml文件时,会去加载spring.handlers配置文件,然后寻找能够解析自定义标签的handler。
spring.handlers在Dubbo中是怎样的内容呢,我们一起来看下:
路径在:dubbo-config/dubbon-config-spring/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
我们来看一下spring是如何找到这个文件并生成对应标签namespaceHandler的:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"application.xml"});
我们使用spring框架,启动的时候都会写这么一句话,加载xml配置文件.当执行到AbstractXmlApplicationContext类的loadBeanDefinitions方法时,会创建一个XmlBeanDefinitionReader对象,读取读取并解析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.
接下来调用XmlBeanDefinitionReader的registerBeanDefinitions方法创建BeanDefinitionDocumentReader对象,这个对象才是真正解析XML的对象。
public int registerBeanDefinitions(Document doc,Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
//创建ReaderContext,并调用documentReader的registerBeanDefinitions方法
documentReader.registerBeanDefinitions(doc,createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
大家再来看下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());
}
终于出现了NamespaceHandler,那就是getNamespaceHandlerResolver()方法,再来看下这个方法
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);
}
//最终会调用这个构造器实例化DefaultNamespaceHandlerResolver类,此时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配置文件的内容
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;
}
会创建一个默认的DefaultNamespaceHandlerResolver对象,其中有一个变量DEFAULT_HANDLER_MAPPINGS_LOCATION,其值是Meta-INF/spring.handlers,此时可以看到spring会去加载spring.hadlers中的内容。
那加载完后,会在什么地方使用呢?
我们来看一下DefaultBeanDefinitionDocumentReader类中的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;
//判断标签是标准的标准还是自定义的,判断的依据就是标准的namespace是否是http://www.springframework.org/schema/beans
//如果是,则是标准的标签,如果不是则不是标准的标签
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele,delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
接下来看一下BeanDefinitionParserDelegate类的parseCustomElement方法,BeanDefinitionParserDelegate对象是在DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions方法中生成的
,它的作用是用来解析标签的。
public BeanDefinition parseCustomElement(Element ele,BeanDefinition containingBd) {
//根据标签得到标签的namespace
//dubbo自定义的标签得到的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,containingBd));
}
接下来看下DefaultBeanDefinitionDocumentReader的resolve方法
@Override
public NamespaceHandler resolve(String namespaceUri) {
//得到加载的内容
Map<String,Object> handlerMappings = getHandlerMappings();
//根据标签的namspace得到handler类名
//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");
}
//实例化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实例后,调用其init()方法
@Override
public void init() {
//将application、module等名称做为key注册到解析器Map中
//这些key名称正是dubbo自定义标签的localName,例如<dubbo:application />标签的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)对应的标签解析器都是DubboBeanDefinitionParser实例对象,至于DubboBeanDefinitionParser是如何解析标签的,这里就不做分析了,如果想要了解,可以看看他的源码。
如果不做特别说明,此篇往后所有的Dubbo源码分析都是基于Dubbo2.7.0