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


    这篇文章是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,具体的标签解析器都继承这个抽象类,我们来看下有哪些:
    NamespaceHandler

    可以看见我们熟悉的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

  • 相关阅读:
    如何获取SQL Server数据库连接字符串的某些部分
    .NET同步原语Barrier简介
    模版引擎RazorEngine简介
    如何使用SQL Server实现SignalR的横向扩展
    SignalR的客户端.NET Client介绍
    一个简单的SignalR例子
    看视频学SignalR—在微软虚拟学院学习SignalR
    看视频学Bootstrap—在微软虚拟学院学习Bootstrap
    C# 窗口与控件的相关操作
    opencv——常见的操作
  • 原文地址:https://www.cnblogs.com/mycodingworld/p/dubbo_xmlreader.html
Copyright © 2020-2023  润新知