服务注册是指将服务暴露出来的过程,包括了服务解析、服务启动、服务注册三部分。其中服务解析就是将 Dubbo 的服务配置解析成 Spring 的 Bean对象;服务启动是启动一个可以处理请求的服务;服务注册是指将服务信息保存到注册中心中,供服务消费方获取。Dubbo 的注册中心支持 Redis、Zookeeper等等,下面以 Zookeeper 为注册中心来解析服务注册的原理。
Spring 自定义标签的实现
Dubbo 中 Provider 提供服务时会使用 <dubbo:service>
标签,这是一个自定义的 Spring标签,那么服务解析的过程就是将配置的 <dubbo:service>
标签解析为 serviceBean
。
在使用一些依赖于 Spring 的组件时,发现可以通过自定义配置 Spring的标签来实现插件的注入,例如数据库源配置、Mybatis配置等等。这里先介绍下 Spring 中自定义标签的方法。
Spring 的标签配置是通过 XML 语言来实现的,通过 XSD(xml schema definition) 来定义元素、属性、数据类型等。Spring 在解析标签时会判断是基本标签(import、alias、bean、beans)还是自定义标签,自定义标签会去查找自定义的解析方法然后解析。
自定义标签的步骤
- 创建一个需要扩展的组件;
- 定义一个 XSD 文件(
org.xsd/soa.xsd
)用于描述组件的内容; - 创建一个实现
AbstractSingleBeanDefinitionParser
接口(org.xsd格式),或者是实现了BeanDefinitionParser
接口(soa.xsd格式)的类,用来解析 XSD文件中的组件定义; - 创建一个 Handler,继承
NamespaceHandlerSupport
,用于将组件注册到 Spring容器; - 编写
Spring.handlers
和Spring.schemas
文件。
AbstractSingleBeanDefinitionParser 实现方式
- 创建组件
public class Car {
private int maxSpeed;
private double price;
private String brand;
private String color;
public Car() {
System.out.println("调用Car类的无参构造函数");
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Car{" +
"maxSpeed=" + maxSpeed +
", price=" + price +
", brand='" + brand + '\'' +
", color='" + color + '\'' +
'}';
}
}
- 定义 XSD文件
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
xmlns="http://hresh.com/schema/org"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://hresh.com/schema/org"
elementFormDefault="qualified">
<xsd:element name="car">
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="maxSpeed" type="xsd:integer" />
<xsd:attribute name="price" type="xsd:double" />
<xsd:attribute name="brand" type="xsd:string" />
<xsd:attribute name="color" type="xsd:string" />
</xsd:complexType>
</xsd:element>
</xsd:schema>
XSD 文件描述组件的结构,它描述了一个新的 targetNameSpace
,并在该空间中定义了一个名称为 car的元素,car有 5个元素可以和组件结构相对应,但没有直接关系。其中 id 属性用来标识每个自定义标签。
- 定义组件解析类
public class CarBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String brand = element.getAttribute("brand");
String color = element.getAttribute("color");
double price = Double.valueOf(element.getAttribute("price"));
int maxSpeed = Integer.valueOf(element.getAttribute("maxSpeed"));
if(StringUtils.hasText(brand)){
builder.addPropertyValue("brand",brand);
}
if(StringUtils.hasText(color)){
builder.addPropertyValue("color",color);
}
builder.addPropertyValue("price",price);
builder.addPropertyValue("maxSpeed",maxSpeed);
}
@Override
protected Class<?> getBeanClass(Element element) {
return Car.class;
}
}
继承 AbstractSingleBeanDefinitionParser
,实现 doParse()
、getBeanClass()
两个方法。
- 将组件注册到 Spring容器
public class MyNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("car", new CarBeanDefinitionParser());
}
}
继承 MyNamespaceHandler
类,该类实现注册组件到 Spring容器。
Spring.handlers
和Spring.schemas
文件
编写上述两个文件并放到 META-INF
文件夹下。
Spring.handlers
:http://hresh.com/schema/org=com.yuqiao.deeplearningdubbo.annotation.MyNamespaceHandler
Spring.schemas
:http://hresh.com/schema/org.xsd=META-INF/org.xsd
Spring 加载自定义组件的流程就是遇到自定义标签,然后去 META-INF
下的 Spring.handlers
、Spring.schemas
文件中找到对应的 handler
和 xsd
,接着调用用户定义的标签解析函数。
- 测试
创建 application.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:myTag="http://hresh.com/schema/org"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://hresh.com/schema/org http://hresh.com/schema/org.xsd">
<myTag:car id="car2" price="56000" maxSpeed="240" brand="宝马" color="银色" />
</beans>
注意标签中 xmlns:myTag
表示 myTag 标签的命名空间是 http://hresh.com/schema/org
。
public class AnnoMain {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Car car = (Car) context.getBean("car2");
System.out.println(car);
}
}
BeanDefinitionParser 实现方式
- 组件沿用第一种定义方式;
- 定义 XSD文件
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
xmlns="http://hresh.com/schema/soa"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
targetNamespace="http://hresh.com/schema/soa"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans" />
<xsd:element name="xxx" >
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="maxSpeed" type="xsd:integer" />
<xsd:attribute name="price" type="xsd:double" />
<xsd:attribute name="brand" type="xsd:string" />
<xsd:attribute name="color" type="xsd:string" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
</xsd:schema>
这次是 soa.xsd
文件,它没有定义 id属性,因为它是继承自 beans:identifiedType
标签,默认带有 id属性。
- 定义组件解析类
public class CarParser implements BeanDefinitionParser {
private Class<?> beanclass;
public CarParser(Class<?> beanclass) {
this.beanclass = beanclass;
}
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClass(beanclass);
beanDefinition.setLazyInit(false);
String brand = element.getAttribute("brand");
String color = element.getAttribute("color");
double price = Double.valueOf(element.getAttribute("price"));
int maxSpeed = Integer.valueOf(element.getAttribute("maxSpeed"));
beanDefinition.getPropertyValues().add("brand", brand);
beanDefinition.getPropertyValues().add("color", color);
beanDefinition.getPropertyValues().add("price", price);
beanDefinition.getPropertyValues().add("maxSpeed", maxSpeed);
BeanDefinitionRegistry registry = parserContext.getRegistry();
String id = element.getAttribute("id");
registry.registerBeanDefinition(id, beanDefinition);
return beanDefinition;
}
}
继承 BeanDefinitionParser
,实现 parse()
方法。
- 将组件注册到 Spring容器
public class MyNamespaceHandler2 extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("xxx", new CarParser(Car.class));
}
}
继承 MyNamespaceHandler
类,该类实现注册组件到 Spring容器。
Spring.handlers
和Spring.schemas
文件
编写上述两个文件并放到 META-INF
文件夹下。
Spring.handlers
:http://hresh.com/schema/soa=com.yuqiao.deeplearningdubbo.annotation.MyNamespaceHandler2
Spring.schemas
:http://hresh.com/schema/soa.xsd=META-INF/soa.xsd
Spring 加载自定义组件的流程就是遇到自定义标签,然后去 META-INF
下的 Spring.handlers
、Spring.schemas
文件中找到对应的 handler
和 xsd
,接着调用用户定义的标签解析函数。
除开上述两种自定义 Spring标签外,还可以自定义 Property
标签用在 Bean对象属性的注入。