Dubbo Bean load

Dubbo is used in conjunction with Spring, and the most common way to do this is to configure dubbo in XML. Let’s see how Spring loads Dubbo beans. Let’s look at a basic configuration of Dubbo beans. The following example is from the official example

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:property-placeholder/>

    <dubbo:application name="demo-provider"/>

    <dubbo:registry address="Zookeeper: / / ${zookeeper. Address: 127.0.0.1} : 2181"/>

    <bean id="demoService" class="org.apache.dubbo.samples.serialization.impl.DemoServiceImpl"/>

    <dubbo:service interface="org.apache.dubbo.samples.serialization.api.DemoService" ref="demoService"
                   serialization="java"/>

</beans>
Copy the code

  • Write a Java Bean;
  • Write an XSD file;
  • Write tag parser, implement BeanDefinitionParser interface;
  • Write NamespaceHandlerSupport to register tag parser, inherit NamespaceHandlerSupport, rewrite init method, register custom tag parser;
  • Write spring. Handlers and spring. Schemas;

Once you’ve developed your custom tags, you can use them in your Spring configuration file. Through the dubbo spring. The handlers file find the class tag parser with com. Alibaba. Dubbo. Config. Spring. The schema. DubboNamespaceHandler. By the following code you can see, the dubbo registered 10 custom tags, labels, in addition to an annotation DubboBeanDefinitionParser parser parsed should be used in other tags, After parsing Dubbo’s Spring custom tags, you create objects and assign properties during spring container startup.

public class DubboNamespaceHandler extends NamespaceHandlerSupport {

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

    @Override
    public void init(a) {
        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".newAnnotationBeanDefinitionParser()); }}Copy the code

The core classes for Dubbo producers and consumers are ServiceBean and ReferenceBean, respectively, which are loaded when Spring parses the < Dubbo :service or < Dubbo: Reference tag. The inheritance structure of these two classes is as follows:

  • ServiceBean

  • ReferenceBean

Dubbo start

In the dubbo bean loading section above, we learned that Dubbo loads beans through Spring custom tags during application startup. Let’s look at how Dubbo starts.

Start listening DubboBootstrapApplicationListener Dubbo client

The previous section described how dubbo’s core beans have been created through spring custom tags, and dubbo’s client is started. Dubbo uses the ApplicationListener interface, which implements Spring’s listener interface, to listen for events completed by the Refresh of the Spring container. When the Spring container is refreshed, Dubbo listens to perform Dubbo client startup as follows:


public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener
        implements Ordered {

    /**
     * The bean name of {@link DubboBootstrapApplicationListener}
     *
     * @since2.7.6 * /
    public static final String BEAN_NAME = "dubboBootstrapApplicationListener";

    private final DubboBootstrap dubboBootstrap;

    public DubboBootstrapApplicationListener(a) {
        // Get the DubboBootstrap object, which will load the Environment, ConfigManager, via SPI
        this.dubboBootstrap = DubboBootstrap.getInstance();
    }

    @Override
    public void onApplicationContextEvent(ApplicationContextEvent event) {
        // Handle ContextRefreshedEvent time. This event is the time when the Spring container finishes refreshing
        if (event instanceof ContextRefreshedEvent) {
            onContextRefreshedEvent((ContextRefreshedEvent) event);
        } else if (event instanceof ContextClosedEvent) {
            // Handle ContextClosedEvent time, Spring context close eventonContextClosedEvent((ContextClosedEvent) event); }}// Spring context refresh completion event
    private void onContextRefreshedEvent(ContextRefreshedEvent event) {
        dubboBootstrap.start();
    }

    // The Spring context closure event
    private void onContextClosedEvent(ContextClosedEvent event) {
        dubboBootstrap.stop();
    }

    @Override
    public int getOrder(a) {
        returnLOWEST_PRECEDENCE; }}abstract class OneTimeExecutionApplicationContextEventListener implements ApplicationListener.ApplicationContextAware {

    private ApplicationContext applicationContext;

    public final void onApplicationEvent(ApplicationEvent event) {
        if (isOriginalEventSource(event) && event instanceofApplicationContextEvent) { onApplicationContextEvent((ApplicationContextEvent) event); }}/**
     * The subclass overrides this method to handle {@link ApplicationContextEvent}
     *
     * @param event {@link ApplicationContextEvent}
     */
    protected abstract void onApplicationContextEvent(ApplicationContextEvent event);

    /**
     * Is original {@link ApplicationContext} as the event source
     *
     * @param event {@link ApplicationEvent}
     * @return* /
    private boolean isOriginalEventSource(ApplicationEvent event) {
        return (applicationContext == null) // Current ApplicationListener is not a Spring Bean, just was added
                // into Spring's ConfigurableApplicationContext
                || Objects.equals(applicationContext, event.getSource());
    }

    @Override
    public final void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public ApplicationContext getApplicationContext(a) {
        returnapplicationContext; }}Copy the code

After the Spring container is refreshed, the event code is as follows:


	/**
	 * Finish the refresh of this context, invoking the LifecycleProcessor's
	 * onRefresh() method and publishing the
	 * {@link org.springframework.context.event.ContextRefreshedEvent}.
	 */
	protected void finishRefresh(a) {
		// Initialize lifecycle processor for this context.
		initLifecycleProcessor();

		// Propagate refresh to lifecycle processor first.
		getLifecycleProcessor().onRefresh();

		// Publish the final event. Publish ContextRefreshedEvent
		publishEvent(new ContextRefreshedEvent(this));

		// Participate in LiveBeansView MBean, if active.
		LiveBeansView.registerApplicationContext(this);
	}
Copy the code

Dubbo start DubboBootstrap# start ()

    public DubboBootstrap start(a) {
        // Whether it has been started
        if (started.compareAndSet(false.true)) {
            ready.set(false);
            / / initialization
            initialize();
            if (logger.isInfoEnabled()) {
                logger.info(NAME + " is starting...");
            }
            Publish the Dubbo service
            exportServices();

            // Not only Provider register, the original data is published once
            if(! isOnlyRegisterProvider() || hasExportedServices()) {// 2. Publish metadata
                exportMetadataService();
                //3. Register the local ServiceInstance if required
                registerServiceInstance();
            }

            referServices();
            if (asyncExportingFutures.size() > 0) {
                new Thread(() -> {
                    try {
                        this.awaitFinish();
                    } catch (Exception e) {
                        logger.warn(NAME + " exportAsync occurred an exception.");
                    }
                    ready.set(true);
                    if (logger.isInfoEnabled()) {
                        logger.info(NAME + " is ready.");
                    }
                }).start();
            } else {
                ready.set(true);
                if (logger.isInfoEnabled()) {
                    logger.info(NAME + " is ready."); }}if (logger.isInfoEnabled()) {
                logger.info(NAME + " has started."); }}return this;
    }
Copy the code

conclusion

Dubbo + Spring’s Bean loading process uses custom tags to load beans. Dubbo spring-based custom tag standard implements the parser DubboBeanDefinitionParser and AnnotationBeanDefinitionParser, through the bean label parsing created, assignment and initialization of the life cycle of treatment, Dubbo implements the Spring listener ApplicationListener, which listens for events when the Spring container is refreshed, and starts the Dubbo client after the Spring container is refreshed to register, publish, subscribe, etc.