1 概述
dubbo是一个简单易用的RPC框架,通过简单的提供者,消费者配置就能完成无感的网络调用。那么在dubbo中是如何将提供者的服务暴露出去,消费者又是如何获取到提供者相关信息的呢?这就是本章我们要讨论的内容。
2 Spring中自定义Schema
在了解dubbo的服务注册和服务发现之前,我们首先需要掌握一个知识点:Spring中自定义Schema。
dubbo Provider 在容器启动后开始暴露服务,并准备接受请求处理。所以可以理解为容器的启动带动dubbo Provider开始工作。
Dubbo 现在的设计是完全无侵入,也就是使用者只依赖于配置契约。在 Dubbo 中,可以使用 XML 配置相关信息来引入服务或者导出服务(也可以使用注解)。配置完成,启动工程,Spring 会读取配置文件,生成注入相关Bean。那 Dubbo 如何实现自定义 XML 被 Spring 加载读取呢?(涉及到dubbo和spring的集成)
从 Spring 2.0 开始,Spring 开始提供了一种基于 XML Schema 格式扩展机制,用于自定义和配置 bean,从而被spring框架所解析。
2.1 案例使用
学习和使用Spring XML Schema 扩展机制并不难,需要下面几个步骤:
- 创建配置属性的JavaBean对象
- 创建一个 XML Schema 文件,描述自定义的合法构建模块,也就是xsd文件
- 自定义处理器类,并实现
NamespaceHandler
接口。 - 自定义解析器,实现
BeanDefinitionParser
接口(最关键的部分)。 - 编写Spring.handlers和spring.schemas文件配置所有部件
定义JavaBean对象,在spring中此对象会根据配置自动创建
public class User { private String id; private String name; private Integer age; //省略getter setter方法 }
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns="http://www.itheima.com/schema/user" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans" targetNamespace="http://www.itheima.com/schema/user" elementFormDefault="qualified" attributeFormDefault="unqualified"> <xsd:import namespace="http://www.springframework.org/schema/beans" /> <xsd:element name="user"> <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>
Spring读取xml文件时,会根据标签的命名空间找到其对应的NamespaceHandler,我们在NamespaceHandler内会注册标签对应的解析器BeanDefinitionParser。
package com.itheima.schema; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; public class UserNamespaceHandler extends NamespaceHandlerSupport { public void init() { registerBeanDefinitionParser("user", new UserBeanDefinitionParser()); } }
BeanDefinitionParser是标签对应的解析器,Spring读取到对应标签时会使用该类进行解析;
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { protected Class getBeanClass(Element element) { return User.class; } 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(id)) { bean.addPropertyValue("id", id); } if (StringUtils.hasText(name)) { bean.addPropertyValue("name", name); } if (StringUtils.hasText(age)) { bean.addPropertyValue("age", Integer.valueOf(age)); } } }
定义spring.handlers文件,内部保存命名空间与NamespaceHandler类的对应关系;必须放在classpath下的META-INF文件夹中。
http\://www.itheima.com/schema/user=com.itheima.schema.UserNamespaceHandler
定义spring.schemas文件,内部保存命名空间对应的xsd文件位置;必须放在classpath下的META-INF文件夹中。
http\://www.itheima.com/schema/user.xsd=META-INF/user.xsd
代码准备好了之后,就可以在spring工程中进行使用和测试,定义spring配置文件,导入对应约束
<?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:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" xmlns:task="http://www.springframework.org/schema/task" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:itheima="http://www.itheima.com/schema/user" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.itheima.com/schema/user http://www.itheima.com/schema/user.xsd"> <itheima:user id="user" name="zhangsan" age="12"></itheima:user> </beans>
编写测试类,通过spring容器获取对象user
public class SchemaDemo { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("/spring/applicationContext.xml"); User user = (User)ctx.getBean("user"); System.out.println(user); } }
2.2 dubbo中的相关对象
Dubbo是运行在spring容器中,dubbo的配置文件也是通过spring的配置文件applicationContext.xml来加载,所以dubbo的自定义配置标签实现,其实同样依赖spring的xml schema机制
1、在dubbo-demo-xml
模块中可以查看dubbo如何在spring的配置文件中进行配置
2、spring如何来解析这些配置并注册对应的bean呢?通过前面的知识我们知道了是通过spring 的 Schema机制
找到dubbo-config/dubbo-config-spring
模块,
核心在DubboNamespaceHandler
中,
可以看出Dubbo所有的组件都是由DubboBeanDefinitionParser
解析,并通过registerBeanDefinitionParser方法来注册到spring中最后解析对应的对象。这些对象中我们重点关注的有以下两个:
- ServiceBean:服务提供者暴露服务的核心对象
- ReferenceBean:服务消费者发现服务的核心对象
- RegistryConfig:定义注册中心的核心配置对象