您的位置:首页 > 编程语言 > Java开发

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

2018-07-23 10:19 597 查看

这篇文章是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提供给使用者实例化PoJo类的。那么dubbo定义的标签spring是如何识别的呢?
其实spring-beans jar包中提供了一个口,那就是NamespaceHandler,它的定义如下:

(省略描述....)
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, this, 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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: