• dubbo源码解析之dubbo配置解析


    文章系列

    一、dubbo源码解析之框架粗谈
    二、dubbo源码解析之dubbo配置解析
    三、dubbo源码解析之服务发布与注册
    四、dubbo源码解析之服务发现
    五、dubbo源码解析之服务调用(通信)流程
    六、dubbo获取服务提供者IP列表

    一、Spring 解析自定义配置

    我们在 Spring 的 xml 配置文件里经常定义各种各样的配置(tx、bean、mvc、bean等等),以及集成第三方框架时,也会看到一些 Spring 之外的配置,例如 mybatis 的配置、dubbo 的配置、redis 的配置等等。
    看到这里,是否会引发我们思考,Spring 是如何解析这些 xml 配置文件呢???

    在 Spring 我们通过创建一个 ClassPathXmlApplicationContext 来解析、加载以及初始化 Spring 上下文,其构造方法代码如下:

    public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
    	public ClassPathXmlApplicationContext(
    			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
    			throws BeansException {
    
    		super(parent);
    		setConfigLocations(configLocations);
    		if (refresh) {
    			// 重启、刷新、重置
    			refresh();
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在其构造方法中,会调用一个 refresh() 方法,进行 Spring 重启、刷新、重置动作,这也是 Spring 的入口方法。

    refresh() 方法中,会加载扫描所有 META-INF/spring.handlers 文件,调用链路如下:

    ClassPathXmlApplicationContextAbstractApplicationContext#refresh()    // 刷新AbstractApplicationContext#obtainFreshBeanFactory()AbstractRefreshableApplicationContext#refreshBeanFactory()		// 刷新AbstractXmlApplicationContext#loadBeanDefinitions(DefaultListableBeanFactory beanFactory)	// 加载 BeanDefinitionAbstractXmlApplicationContext#loadBeanDefinitions(XmlBeanDefinitionReader reader)		// 加载xml 中的 BeanDefinitionAbstractBeanDefinitionReader#loadBeanDefinitions(Resource... resources)XmlBeanDefinitionReader#loadBeanDefinitions(Resource resource)XmlBeanDefinitionReader#loadBeanDefinitions(EncodedResource encodedResource)XmlBeanDefinitionReader#doLoadBeanDefinitions(InputSource inputSource, Resource resource)		// 加载xml 中的 BeanDefinitionXmlBeanDefinitionReader#registerBeanDefinitions(Document doc, Resource resource)		// 解析doc,注册对应的 BeanDefinitionDefaultBeanDefinitionDocumentReader#registerBeanDefinitions(Document doc, XmlReaderContext readerContext)DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions(Element root)	// 解析doc,注册对应的 BeanDefinition
    
    DefaultBeanDefinitionDocumentReader#parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)	// 解析BeanDefinitionParserDelegate#parseCustomElement(Element ele)	// 没有使用Spring默认的XML命名空间,则使用用户自定义的解析规则解析元素节点
    																// 如果使用Spring的Bean规则解析元素节点,则调用DefaultBeanDefinitionDocumentReader#parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) 方法BeanDefinitionParserDelegate#parseCustomElement(Element ele, @Nullable BeanDefinition containingBd)DefaultNamespaceHandlerResolver#resolve(String namespaceUri)	// 根据namespaceUri 获取对应的NamespaceHandlerDefaultNamespaceHandlerResolver#getHandlerMappings()	// 获取所有 handlerMappings 映射
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    DefaultNamespaceHandlerResolver#getHandlerMappings() 方法,获取所有 handlerMappings 映射。

    public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
    	// 默认Handler映射文件
    	public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
    	
    	public DefaultNamespaceHandlerResolver() {
    		this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
    	}
    	
    	private Map<String, Object> getHandlerMappings() {
    		Map<String, Object> handlerMappings = this.handlerMappings;
    		if (handlerMappings == null) {
    			synchronized (this) {
    				handlerMappings = this.handlerMappings;
    				if (handlerMappings == null) {
    					try {
    						// 将classpath下的 META-INF/spring.handlers 中的配置加载到Map中
    						// this.handlerMappingsLocation = META-INF/spring.handlers
    						Properties mappings =
    								PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
    						if (logger.isDebugEnabled()) {
    							logger.debug("Loaded NamespaceHandler mappings: " + mappings);
    						}
    						Map<String, Object> mappingsToUse = new ConcurrentHashMap<>(mappings.size());
    						CollectionUtils.mergePropertiesIntoMap(mappings, mappingsToUse);
    						
    						// key   -> NamespaceUrl
    						// value -> NamespaceHandler
    						handlerMappings = mappingsToUse;
    						this.handlerMappings = handlerMappings;
    					}
    					catch (IOException ex) {
    						throw new IllegalStateException(
    								"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
    					}
    				}
    			}
    		}
    		return handlerMappings;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    Spring 在启动过程中,将所有 classpath 下的 META-INF/spring.handlers 文件中的配置加载到一个 Map handlerMappings 中,该 Map 存储了从命名空间 URI 到 NamespaceHandler 类名实例的映射,其中 NamespaceUrl 为key,对应的 NamespaceHandler 为value,例如 dubbo 中的 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
    
    • 1
    • 2

    最后,会在 DefaultNamespaceHandlerResolver#resolve(String namespaceUri) 方法中,调用 handlerMappings 中的 NamespaceHandlerinit() 方法,逻辑如下:

    public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
    	public NamespaceHandler resolve(String namespaceUri) {
    		// 获取handlerMappings
    		Map<String, Object> handlerMappings = getHandlerMappings();
    	
    		// 通过 namespaceUri,获取对应的 NamespaceHandler
    		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);
    				// 调用其 init() 方法,完成初始化
    				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);
    			}
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    二、编写一个 Spring 自定义配置

    完成一个Spring的自定义配置一般需要下面几步:

    1. 定义Java Bean
    2. 编写XSD(XML Schema)文件,用于校验XML
    3. 编写 NameSpaceHandler 和 BeanDefinitionParser 完成解析工作
    4. 编写 spring.handlersspring.schemas 串联起所有部件
    5. 编写自定义配置文件xml
    6. 应用

    2.1 定义Java Bean

    @Data
    public class People {
        private String id;
        private String name;
        private Integer age;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.2 编写XSD(XML Schema)文件

    用于校验XML,定义了一些列的语法来规范XML,存放在 resources/META-INF/ 目录下,如:
    在这里插入图片描述
    people.xsd 内容如下:

    <xsd:schema xmlns="http://code.test.com/schema/people"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:beans="http://www.springframework.org/schema/beans"
            targetNamespace="http://code.test.com/schema/people"
            elementFormDefault="qualified">
     
            <xsd:import namespace="http://www.springframework.org/schema/beans"/>
            
            <xsd:element name="people">
                <xsd:complexType>
                    <xsd:complexContent>
                        <xsd:extension base="beans:identifiedType">
                            <xsd:attribute name="name" type="xsd:string"/>
                            <xsd:attribute name="age" type="xsd:int"/>
                        xsd:extension>
                    xsd:complexContent>
                xsd:complexType>
            xsd:element>
    xsd:schema>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2.3 编写 NameSpaceHandler 和 BeanDefinitionParser 完成解析工作

    NameSpaceHandler

    import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
     
    public class MyNamespaceHandler extends NamespaceHandlerSupport {
        @Override
        public void init() {
            registerBeanDefinitionParser("people", new PeopleBeanDefinitionParser());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    PeopleBeanDefinitionParser

    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
    import org.springframework.util.StringUtils;
    import org.w3c.dom.Element;
     
    public class PeopleBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
        @Override
        protected Class getBeanClass(Element element){
            return People.class;
        }
        @Override
        protected void doParse(Element element, BeanDefinitionBuilder bean){
            String name = element.getAttribute("name");
            String age = element.getAttribute("age");
            String id = element.getAttribute("id");
            if (StringUtils.hasText(name)) {
                bean.addPropertyValue("name",name);
            } if (StringUtils.hasText(age)) {
                bean.addPropertyValue("age",Integer.valueOf(age));
            } if (StringUtils.hasText(id)) {
                bean.addPropertyValue("id",id);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    2.4 编写 spring.handlersspring.schemas

    编写 spring.handlersspring.schemas ,用于串联所有部件,存放在 resources/META-INF/ 目录下,如:
    在这里插入图片描述

    spring.handlers 内容如下:

    http\://code.test.com/schema/people=com.example.schema.MyNamespaceHandler
    
    • 1

    spring.schemas 内容如下:

    http\://code.test.com/schema/people.xsd=META-INF/people.xsd
    
    • 1

    2.5 编写自定义配置文件xml

    <?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:test="http://code.test.com/schema/people"
           xsi:schemaLocation="
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://code.test.com/schema/people http://code.test.com/schema/people.xsd
     
    ">
        <test:people id="people" name="testName" age="100"/>
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.6 应用

    import org.springframework.context.support.ClassPathXmlApplicationContext;
     
    public class SchemaMain {
        public static void main(String[] args) {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/MyName.xml");
            People people =(People) context.getBean("people");
            System.out.println(people.getName());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    三、dubbo 中的 Spring 配置

    dubbo 通过 Spring 提供的自定义配置解析,完成了与 Spring 的整合,内容如下:
    在这里插入图片描述
    其中 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
    
    • 1
    • 2

    3.1 DubboNamespaceHandler

    Spring 在装载 DubboNamespaceHandler 时,会自动调用其 init() 方法,通过注册一个BeanDefinitionParser 解析器,完成Bean对象的注册,如下:。

    public class DubboNamespaceHandler extends NamespaceHandlerSupport implements ConfigurableSourceBeanMetadataElement {
    	@Override
        public void init() {
        	// 注册 Bean 定义解析器
            registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
            registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
            registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
            registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));
            registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true));
            registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
            registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true));
            registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.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, true));
            registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3.2 DubboBeanDefinitionParser

    Spring 会自动调用 BeanDefinitionParser 中的 BeanDefinition parse(Element element, ParserContext parserContext) 方法,解析指定的 Element ,并注册到其 IOC 容器中。

    public class DubboBeanDefinitionParser implements BeanDefinitionParser {
    	@Override
        public BeanDefinition parse(Element element, ParserContext parserContext) {
        	// 解析
            return parse(element, parserContext, beanClass, required);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    参考文档https://www.cnblogs.com/huan30/p/12790406.html

  • 相关阅读:
    嵌入式开发:嵌入式基础——代码和数据空间揭秘
    【必会篇】日常测试工作中,有哪些必须知道的 SQL 语句?
    bash: cmake: command not found...+++++++lsb_release: command not found
    大厂真题:【链表】大疆2023秋招-链表合并
    Linux下安装Docker(centOS 8)
    【下载器】NDM和IDM介绍(含安装包和教程)
    【python爬虫】批量识别pdf中的英文,自动翻译成中文上
    复习Day01:数组part01:701. 二分查找、35. 搜索插入位置、367. 有效的完全平方数、69. x的平方根、74. 搜索二维矩阵
    【C++】拷贝构造函数调用时机 ① ( 使用一个对象初始化另外一个对象 | 将一个对象赋值给另外一个对象 )
    20个实用Python自动化脚本技巧
  • 原文地址:https://blog.csdn.net/qq_33375499/article/details/126474451