Dubbo基础内容

一、基础内容

1.1 介绍

        Dubbo是一款高性能、轻量级的开源 Java RPC 远程过程调用框架。其拥有面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现的能力。RPC指的是远程过程调用,一种进行通信方式。 它允许程序调用另一个地址空间(网络的另一台机器上)的过程或函 数,而不用开发人员显式编码这个调用的细节。

        Dubbo架构图:

        Dubbo的角色主要是:

  • 服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务;
  • 服务消费者(Consumer): 调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一 台提供者进行调用,如果调用失败,再选另一台调用;
  • 注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者;
  • 监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

        根据架构图中的内容,对应其中编号0-5的步骤,其调用行为主要包括:

        〇服务容器负责启动、加载、运行服务提供者;

        ①服务提供者在启动时,向注册中心注册自己提供的服务;

        ②服务消费者在启动时,向注册中心订阅自己所需的服务;

        ③注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者;

        ④服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用;

        ⑤服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

1.2 直连案例

1.2.1 案例代码

        要实现整个案例,首先便是要定义架构图中的两个主要角色:服务提供者和服务发现者,分别使用link-privider和link-consumer两个module来进行演示。

        首先对服务提供者而言,要开启Dubbo,就要新建Maven工程,然后在pom.xml中导入依赖:

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.9.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.9.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.alibaba/dubbo -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>dubbo</artifactId>
      <version>2.6.2</version>
    </dependency>

        配置接口和实现类:

// Hello接口
public interface SomeServices {
    String hello(String msg);
}

// 实现类
public class SomeServiceImpl implements SomeServices {
    @Override
    public String hello(String msg) {
        return "Hello " + msg;
    }
}

        在resources目录下,新建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:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

<!--    声明服务提供者的名称,保证唯一性,是Dubbo的唯一标识-->
    <dubbo:application name="link-provider"/>
<!--    指定Dubbo的协议名称和端口号
    端口号默认20880
-->
    <dubbo:protocol name="dubbo" port="20880"/>
<!--暴露服务
    interface:暴露服务的接口全限定类名
    ref:引用接口在Spring容器中的标识名称
    registry:使用直连方式,不使用注册中心,则配置为N/A
-->
    <dubbo:service interface="com.demo.dubbo.service.SomeServices" ref="someServiceImpl" register="N/A"/>
<!--    加载接口实现类-->
    <bean id="someServiceImpl" class="com.demo.dubbo.service.impl.SomeServiceImpl"/>
</beans>

        然后便是配置xml配置文件生效:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                  http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
  <display-name>Archetype Created Web Application</display-name>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:dubbo-link-provider.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
</web-app>

        对于服务消费者而言,最主要的便是如何知道服务提供者暴露的接口。配置服务提供者,关闭打war包,然后进行maven的install操作,打包并发布到本地仓库。

<!--    添加服务提供者接口依赖-->
    <dependency>
      <groupId>org.example</groupId>
      <artifactId>link-provider</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>

        然后便是最重要的:配置消费者的核心配置文件:

<?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://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

<!--    声明服务消费者,保证唯一性-->
    <dubbo:application name="link-consumer"/>

<!--    引用远程接口
    interface:接口的全限定名
    url:调用远程接口服务的url地址
    registry:直连方式,不使用注册中心
-->
    <dubbo:reference id="someService"
                     interface="com.demo.dubbo.service.SomeService"
                     url="dubbo://localhost:20880"
                     registry="N/A"/>
</beans>

        核心配置文件非常重要,关系到消费端如何通过端口与服务端相连接的问题。在所有配置完成,服务启动后,也会看到:

        消费端在启动时,就会根据核心配置文件,进行服务端接口调用的全类名拼接,从而开启远程调用。

        然后,如果是使用SpringMVC,那需要编写的Controller代码和xml配置如下:

@Controller
public class SomeController {

    @Autowired
    private SomeService someService;

    @RequestMapping(value = "/hello")
    public String hello(Model model){
        // 调用远程接口服务
        String hello = someService.hello("Dubbo");
        model.addAttribute("hello",hello);
        return "hello";
    }
}
<?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:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!--    扫描组件-->
    <context:component-scan base-package="com.demo.dubbo.web"/>

<!--    注解驱动-->
    <mvc:annotation-driven/>

<!--    视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value=""/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

        最后是配置中央调试器:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                  http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:dubbo-link-consumer.xml,classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

        全部配置好之后,将服务端和消费端依次启动即可。

1.2.2 案例解读

        直连方式简化了开发流程,不需要再配置注册中心。但是直连方式不仅仅能获得远程接口,还能直接获得接口的实现类,从而知道其实现细节。这是很不好的一个点。这与Dubbo服务以接口为粒度,为开发者屏蔽远程调用细节相违背。

        当然,如果要对直连模式进行修改,官方有推荐的项目结构:就是在服务提供者和服务消费者的基础上,再增加接口工程,专门用来存储业务接口和实体类。

1.2.3 增加接口工程

        为了进一步优化项目,增加接口工程,然后将实体类和接口都放在接口工程中。再让服务提供者依赖接口工程,实现其接口,最后由服务消费者调用。其中,服务提供者配置Maven:

    <dependency>
      <groupId>com.demo.dubbo</groupId>
      <artifactId>link-interface</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>

        对服务提供者和服务消费者而言,其他配置不变。但是此处会有一个坑,就是实体类的序列化问题。对自定义的实体类,如果不implements Serializable,那就会报错,这里是需要注意的点。

1.3 Dubbo的其他配置

1.3.1 关闭检查

        Dubbo会在启动时检查依赖的服务是否可用。如果不可用会抛出异常,阻止Spring初始化完成。默认check=true。可以进行配置:

<!-- 关闭某个服务的启动时检查 -->
<dubbo:reference interface="com.foo.BarService" check="false" />

<!-- 关闭注册中心的启动时检查 -->
<dubbo:registry check="false" />

        这个配置一般是应用在开发环境。因为在开发过程中可能会有未开发完的功能,存在服务不可用的情况。此时就需要关闭检查,从而对已完成的部分进行开发测试,而不会因为异常而无法测试。

1.3.2 重试次数

        消费者访问提供者,如果访问失败,则切换重试访问其它服务器,但重试会带来更长延迟。 访问时间变长,用户的体验较差。这个配置项感觉比较鸡肋,一般来说设为一次即可。如果不能访问,那多试很多次,那成功的概率也会比较低,且还会大大增加延迟。

<!-- 重试次数配置如下: -->
<dubbo:service retries="2" />
<dubbo:reference retries="2" />

1.3.3 超时时间

        由于网络或服务端不可靠,会导致调用出现一种不确定的中间状态(超时)。为了避免超时导致客户端资源(线程)挂起耗尽,必须设置超时时间。配置方式:

<!-- 服务消费者配置 -->
<dubbo:reference interface="com.foo.BarService" timeout="2000" />
<!-- 服务提供者配置 -->
<dubbo:server interface="com.foo.BarService" timeout="2000" />

1.3.4 版本号

        每个接口都应定义版本号,为后续不兼容升级提供可能。当一个接口有不同的实现,项目早期使用的一个实现类, 之后创建接口的新的实现类。区分不同的接口实现使用version。 特别是项目需要把早期接口的实现全部换位新的实现类,也需要使用version。

<double:service interface="com.demo.dubbo.service.SomeService" 
                ref="someServiceImpl" 
                version="1.0"/>
<dubbo:reference id="someService"
                     interface="com.demo.dubbo.service.SomeService"
                     url="dubbo://localhost:20880"
                     registry="N/A"
                     version="1.0"/>

二、原理

2.1 RPC原理

        RPC的同步调用流程可以用一张图来进行说明:

        其中,Computer01是Client服务消费方,而Computer02是Server服务提供方。具体过程如下:

        1)rpc call:Client以本地调用方式调用服务;

        2)bundle Args:Client Stub接收到调用后,将方法、参数等组成为能进行网络传输的消息体;

        3)send:Client Stub找到服务地址,将消息通过网络服务发送给服务端;

        4)receive + unbundle args:服务端的Server Stub收到消息后进行解码;

        5)local call:Server Stub根据解码结果调用本地服务;

        6)local return:本地服务之星并将结果返回给Server Stub;

        7)send:Server Stub将返回结果打包成消息发送给服务消费方;

        8)receive + unbundle ret vals:Client Stub收到消息,进行解码;

        9)rpc return:Client得到最终结果。

        以上过程中,2-8过程是被RPC框架封装的,对用户来说是不可见的。这些过程也就是RPC框架的基本原理。至于实现细节,不同的框架会有出入,但是总体来说提供的功能都是类似的。

2.2 Dubbo原理

2.2.1 框架设计

        Dubbo官方对框架设计就有详细的解读文档:

  • Server 服务层:对外提供调用的接口;
  • config 配置层:对外配置接口,以 ServiceConfig, ReferenceConfig 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类;
  • proxy 服务代理层:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 ServiceProxy 为中心,扩展接口为 ProxyFactory;
  • registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactory, Registry, RegistryService;
  • cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster, Directory, Router, LoadBalance;
  • monitor 监控层:RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory, Monitor, MonitorService;
  • protocol 远程调用层:封装 RPC 调用,以 Invocation, Result 为中心,扩展接口为 Protocol, Invoker, Exporter;
  • exchange 信息交换层:封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer;
  • transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel, Transporter, Client, Server, Codec;
  • serialize 数据序列化层:可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput, ThreadPool;

        Dubbo自己的jar包排列也是根据其框架来进行排布的:

2.2.2 解析、加载配置信息

        Spring解析配置文件中每个标签,有一个总接口:BeanDefinitionParser。

        通过查看其继承树,可以看出,DubboBeanDefinitionParser就是对其进行的实现,专用于Dubbo的专用标签解析。解析过程的源码为该类中的parse方法:

private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClass(beanClass);
        beanDefinition.setLazyInit(false);
        // 获取标签id
        String id = element.getAttribute("id");
        // 标签id为空,采用其他方式增加唯一标识
        if ((id == null || id.length() == 0) && required) {
            // 转而获取标签name
            String generatedBeanName = element.getAttribute("name");
            // 如果标签name还为空,则配置使用默认名称
            if (generatedBeanName == null || generatedBeanName.length() == 0) {
                if (ProtocolConfig.class.equals(beanClass)) {
                    generatedBeanName = "dubbo";
                } else {
                    generatedBeanName = element.getAttribute("interface");
                }
            }
            if (generatedBeanName == null || generatedBeanName.length() == 0) {
                generatedBeanName = beanClass.getName();
            }
            // 唯一标识配置完毕
            id = generatedBeanName;
            int counter = 2;
            while (parserContext.getRegistry().containsBeanDefinition(id)) {
                id = generatedBeanName + (counter++);
            }
        }
        if (id != null && id.length() > 0) {
            if (parserContext.getRegistry().containsBeanDefinition(id)) {
                throw new IllegalStateException("Duplicate spring bean id " + id);
            }
            parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
            beanDefinition.getPropertyValues().addPropertyValue("id", id);
        }
        // 进来的配置项为dubbo:protocol,进行此项配置的解读
        if (ProtocolConfig.class.equals(beanClass)) {
            for (String name : parserContext.getRegistry().getBeanDefinitionNames()) {
                BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name);
                PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol");
                if (property != null) {
                    Object value = property.getValue();
                    if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) {
                        definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
                    }
                }
            }
        // 进来的配置项为dubbo:service,进行此项配置的解读
        } else if (ServiceBean.class.equals(beanClass)) {
            String className = element.getAttribute("class");
            if (className != null && className.length() > 0) {
                RootBeanDefinition classDefinition = new RootBeanDefinition();
                classDefinition.setBeanClass(ReflectUtils.forName(className));
                classDefinition.setLazyInit(false);
                parseProperties(element.getChildNodes(), classDefinition);
                beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
            }
        // 进来的配置项为ProviderConfig,进行此项配置的解读
        } else if (ProviderConfig.class.equals(beanClass)) {
            parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);
        // 进来的配置项为ConsumerConfig,进行此项配置的解读
        } else if (ConsumerConfig.class.equals(beanClass)) {
            parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);
        }
        Set<String> props = new HashSet<String>();
        ManagedMap parameters = null;
        for (Method setter : beanClass.getMethods()) {
            String name = setter.getName();
            if (name.length() > 3 && name.startsWith("set")
                    && Modifier.isPublic(setter.getModifiers())
                    && setter.getParameterTypes().length == 1) {
                Class<?> type = setter.getParameterTypes()[0];
                String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), "-");
                props.add(property);
                Method getter = null;
                try {
                    getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);
                } catch (NoSuchMethodException e) {
                    try {
                        getter = beanClass.getMethod("is" + name.substring(3), new Class<?>[0]);
                    } catch (NoSuchMethodException e2) {
                    }
                }
                if (getter == null
                        || !Modifier.isPublic(getter.getModifiers())
                        || !type.equals(getter.getReturnType())) {
                    continue;
                }
                if ("parameters".equals(property)) {
                    parameters = parseParameters(element.getChildNodes(), beanDefinition);
                } else if ("methods".equals(property)) {
                    parseMethods(id, element.getChildNodes(), beanDefinition, parserContext);
                } else if ("arguments".equals(property)) {
                    parseArguments(id, element.getChildNodes(), beanDefinition, parserContext);
                } else {
                    String value = element.getAttribute(property);
                    if (value != null) {
                        value = value.trim();
                        if (value.length() > 0) {
                            if ("registry".equals(property) && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) {
                                RegistryConfig registryConfig = new RegistryConfig();
                                registryConfig.setAddress(RegistryConfig.NO_AVAILABLE);
                                beanDefinition.getPropertyValues().addPropertyValue(property, registryConfig);
                            } else if ("registry".equals(property) && value.indexOf(',') != -1) {
                                parseMultiRef("registries", value, beanDefinition, parserContext);
                            } else if ("provider".equals(property) && value.indexOf(',') != -1) {
                                parseMultiRef("providers", value, beanDefinition, parserContext);
                            } else if ("protocol".equals(property) && value.indexOf(',') != -1) {
                                parseMultiRef("protocols", value, beanDefinition, parserContext);
                            } else {
                                Object reference;
                                if (isPrimitive(type)) {
                                    if ("async".equals(property) && "false".equals(value)
                                            || "timeout".equals(property) && "0".equals(value)
                                            || "delay".equals(property) && "0".equals(value)
                                            || "version".equals(property) && "0.0.0".equals(value)
                                            || "stat".equals(property) && "-1".equals(value)
                                            || "reliable".equals(property) && "false".equals(value)) {
                                        // backward compatibility for the default value in old version's xsd
                                        value = null;
                                    }
                                    reference = value;
                                } else if ("protocol".equals(property)
                                        && ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(value)
                                        && (!parserContext.getRegistry().containsBeanDefinition(value)
                                        || !ProtocolConfig.class.getName().equals(parserContext.getRegistry().getBeanDefinition(value).getBeanClassName()))) {
                                    if ("dubbo:provider".equals(element.getTagName())) {
                                        logger.warn("Recommended replace <dubbo:provider protocol=\"" + value + "\" ... /> to <dubbo:protocol name=\"" + value + "\" ... />");
                                    }
                                    // backward compatibility
                                    ProtocolConfig protocol = new ProtocolConfig();
                                    protocol.setName(value);
                                    reference = protocol;
                                } else if ("onreturn".equals(property)) {
                                    int index = value.lastIndexOf(".");
                                    String returnRef = value.substring(0, index);
                                    String returnMethod = value.substring(index + 1);
                                    reference = new RuntimeBeanReference(returnRef);
                                    beanDefinition.getPropertyValues().addPropertyValue("onreturnMethod", returnMethod);
                                } else if ("onthrow".equals(property)) {
                                    int index = value.lastIndexOf(".");
                                    String throwRef = value.substring(0, index);
                                    String throwMethod = value.substring(index + 1);
                                    reference = new RuntimeBeanReference(throwRef);
                                    beanDefinition.getPropertyValues().addPropertyValue("onthrowMethod", throwMethod);
                                } else if ("oninvoke".equals(property)) {
                                    int index = value.lastIndexOf(".");
                                    String invokeRef = value.substring(0, index);
                                    String invokeRefMethod = value.substring(index + 1);
                                    reference = new RuntimeBeanReference(invokeRef);
                                    beanDefinition.getPropertyValues().addPropertyValue("oninvokeMethod", invokeRefMethod);
                                }else {
                                    if ("ref".equals(property) && parserContext.getRegistry().containsBeanDefinition(value)) {
                                        BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value);
                                        if (!refBean.isSingleton()) {
                                            throw new IllegalStateException("The exported service ref " + value + " must be singleton! Please set the " + value + " bean scope to singleton, eg: <bean id=\"" + value + "\" scope=\"singleton\" ...>");
                                        }
                                    }
                                    reference = new RuntimeBeanReference(value);
                                }
                                beanDefinition.getPropertyValues().addPropertyValue(property, reference);
                            }
                        }
                    }
                }
            }
        }
        NamedNodeMap attributes = element.getAttributes();
        int len = attributes.getLength();
        for (int i = 0; i < len; i++) {
            Node node = attributes.item(i);
            String name = node.getLocalName();
            if (!props.contains(name)) {
                if (parameters == null) {
                    parameters = new ManagedMap();
                }
                String value = node.getNodeValue();
                parameters.put(name, new TypedStringValue(value, String.class));
            }
        }
        if (parameters != null) {
            beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);
        }
        return beanDefinition;
    }

        实际进入的BeanClass解析标签会根据标签的配置顺序依次进入parse方法进行解析。 而每种标签具体的类型初始化,是在parse方法之前,在NamespaceHandlerSupport函数中:

public class DubboNamespaceHandler extends NamespaceHandlerSupport {

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

    @Override
    public void init() {
        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());
    }

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值