Dubbo(五)_服务注册原理

发布时间 2023-07-29 23:52:19作者: Stitches

服务注册是指将服务暴露出来的过程,包括了服务解析、服务启动、服务注册三部分。其中服务解析就是将 Dubbo 的服务配置解析成 Spring 的 Bean对象;服务启动是启动一个可以处理请求的服务;服务注册是指将服务信息保存到注册中心中,供服务消费方获取。Dubbo 的注册中心支持 Redis、Zookeeper等等,下面以 Zookeeper 为注册中心来解析服务注册的原理。

Spring 自定义标签的实现

参考:https://zhuanlan.zhihu.com/p/107837020

Dubbo 中 Provider 提供服务时会使用 <dubbo:service> 标签,这是一个自定义的 Spring标签,那么服务解析的过程就是将配置的 <dubbo:service> 标签解析为 serviceBean

在使用一些依赖于 Spring 的组件时,发现可以通过自定义配置 Spring的标签来实现插件的注入,例如数据库源配置、Mybatis配置等等。这里先介绍下 Spring 中自定义标签的方法。

Spring 的标签配置是通过 XML 语言来实现的,通过 XSD(xml schema definition) 来定义元素、属性、数据类型等。Spring 在解析标签时会判断是基本标签(import、alias、bean、beans)还是自定义标签,自定义标签会去查找自定义的解析方法然后解析。

自定义标签的步骤

  1. 创建一个需要扩展的组件;
  2. 定义一个 XSD 文件( org.xsd/soa.xsd )用于描述组件的内容;
  3. 创建一个实现 AbstractSingleBeanDefinitionParser 接口(org.xsd格式),或者是实现了 BeanDefinitionParser 接口(soa.xsd格式)的类,用来解析 XSD文件中的组件定义;
  4. 创建一个 Handler,继承 NamespaceHandlerSupport,用于将组件注册到 Spring容器;
  5. 编写 Spring.handlersSpring.schemas 文件。

AbstractSingleBeanDefinitionParser 实现方式

  1. 创建组件
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 + '\'' +
                '}';
    }
}
  1. 定义 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 属性用来标识每个自定义标签。

  1. 定义组件解析类
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()两个方法。

  1. 将组件注册到 Spring容器
public class MyNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        registerBeanDefinitionParser("car", new CarBeanDefinitionParser());
    }
}

继承 MyNamespaceHandler类,该类实现注册组件到 Spring容器。

  1. Spring.handlersSpring.schemas 文件

编写上述两个文件并放到 META-INF 文件夹下。

Spring.handlershttp://hresh.com/schema/org=com.yuqiao.deeplearningdubbo.annotation.MyNamespaceHandler

Spring.schemashttp://hresh.com/schema/org.xsd=META-INF/org.xsd

Spring 加载自定义组件的流程就是遇到自定义标签,然后去 META-INF 下的 Spring.handlersSpring.schemas文件中找到对应的 handlerxsd,接着调用用户定义的标签解析函数。

  1. 测试

创建 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 实现方式

  1. 组件沿用第一种定义方式;
  2. 定义 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属性。

  1. 定义组件解析类
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() 方法。

  1. 将组件注册到 Spring容器
public class MyNamespaceHandler2 extends NamespaceHandlerSupport {
    @Override
    public void init() {
        registerBeanDefinitionParser("xxx", new CarParser(Car.class));
    }
}

继承 MyNamespaceHandler类,该类实现注册组件到 Spring容器。

  1. Spring.handlersSpring.schemas 文件

编写上述两个文件并放到 META-INF 文件夹下。

Spring.handlershttp://hresh.com/schema/soa=com.yuqiao.deeplearningdubbo.annotation.MyNamespaceHandler2

Spring.schemashttp://hresh.com/schema/soa.xsd=META-INF/soa.xsd

Spring 加载自定义组件的流程就是遇到自定义标签,然后去 META-INF 下的 Spring.handlersSpring.schemas文件中找到对应的 handlerxsd,接着调用用户定义的标签解析函数。

除开上述两种自定义 Spring标签外,还可以自定义 Property 标签用在 Bean对象属性的注入。