1. Integration of Dubbo and Spring to parse configuration files

Keywords: Java Dubbo Spring xml Apache

1. Dubbo consumer invocation service provider example

public class Consumer {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = 
            new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-consumer.xml"});
        context.start();
        DemoService demoService = (DemoService) context.getBean("demoService");

        while (true) {
            try {
                Thread.sleep(1000);
                String hello = demoService.sayHello("world");
                System.out.println(hello); 
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }
    }
}
<?xml version="1.0" encoding="UTF-8"?>

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

    <dubbo:application name="demo-consumer"/>
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
    <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService"/>
</beans>
public class Provider {

    public static void main(String[] args) throws Exception {
        System.setProperty("java.net.preferIPv4Stack", "true");
        ClassPathXmlApplicationContext context = 
          new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
        context.start();
        System.in.read(); 
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <dubbo:application name="demo-provider"/>
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
    <dubbo:protocol name="dubbo" port="20880"/>
    <bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/>
    <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>

</beans>

2. Spring parses dubbo configuration files

_Start the service provider first, then start the consumer, and find that the console can output normally. Here's how Spring parses dubbo's configuration files for consumers and service providers. The Spring container provides IOC functionality to generate beans for us. Usually, we put the definition of beans in an XML file. Let's analyze Spring's process of loading xml configuration files and generating beans. Spring provides two types of containers: BeanFactory and Application Context. BeanFactory is lazy loading, which means delayed initialization. It initializes the bean only when you call getBean, while ApplicationContext is the bean that loads non-delayed initialization when you initialize the container. First, the process of beans generated by Spring container in a simple overview is introduced. First, the information of beans is encapsulated into beanDefinitions through the load BeanDefinition process, and then beans are created according to these beanDefinitions. Let's look at Spring's process of parsing Dubbo's configuration file and generating beans.

//1. Spring container initialization when new ClassPathXml Application Context, at which time the loadBeanDefinition method is called to load
//Parsing xml configuration files
context = 
     new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
//2. Loading configuration files will eventually come here
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
            // 3. In fact, we have parsed xml files into Document s through dom4j, and parsed one configuration in xml into one
            //Node goes to read processing.
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    // 4. Determine whether Spring can handle Node by default. Here's the screenshot below, because dubbo:application
                    //It is defined in dubbo and does not belong to Spring's namespace management
                    if (delegate.isDefaultNamespace(ele)) {
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
}

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
        //http://dubbo.apache.org/schema/dubbo
        String namespaceUri = getNamespaceURI(ele);
        //DubboNameSpaceHandler
        NamespaceHandler handler = 
                             this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler == null) {
            return null;
        }
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
private static BeanDefinition parse(Element element, ParserContext parserContext, 
                                                            Class<?> beanClass, boolean required) {
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        //class com.alibaba.dubbo.config.ApplicationConfig
        beanDefinition.setBeanClass(beanClass);
        beanDefinition.setLazyInit(false);
        //Parsing id attributes
        String id = element.getAttribute("id");
        if ((id == null || id.length() == 0) && required) {
            String generatedBeanName = element.getAttribute("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);
            }
            //Register BeanDefinition
            parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
            //Put id attributes into the bean Definition, and subsequent getBean s create beans based on these attributes.
            //The bean created here is Application Config
            beanDefinition.getPropertyValues().addPropertyValue("id", id);
        }

        //Delete some code. reference is the value value parsed, so you can see that both attribute and attribute values are put in BeanDefinition here.
        beanDefinition.getPropertyValues().addPropertyValue(property, reference);
        return beanDefinition;
}

At this point, Spring parses the application node in xml into a Bean Definition and registers it in Registry, which is a Map. Following is an analysis of Spring's process of creating this Application Config.

3. Spring Creates Application Config

context = 
       new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
// Spring container initialization process, new ClassPath Xml Application Context will come here later
public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            prepareRefresh();
            // This executes the above analysis process, calling loadBeanDefinition to parse BeanDefinition
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            prepareBeanFactory(beanFactory);
            try {
                postProcessBeanFactory(beanFactory);
                invokeBeanFactoryPostProcessors(beanFactory);
                registerBeanPostProcessors(beanFactory);
                initMessageSource();
                initApplicationEventMulticaster();
                onRefresh();
                registerListeners();
                // Instantiate all remaining (non-lazy-init) singletons. You can see the Spring container initialization
                //Later, the non-lazy loaded bean s are initialized, and here we go to the preInstantiasteSingletons method shown below.
                finishBeanFactoryInitialization(beanFactory);
                finishRefresh();
            }
        }
}

//Spring creates bean s that will eventually come here
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, 
                                   final Object[] args) throws BeanCreationException {
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            //Delete some useless code, where reflection is invoked to create a bean, which is only an empty bean, and the attribute has not been assigned.
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);

        Object exposedObject = bean;
        try {
            //Property assignment, and ultimately invoking reflection for assignment
            populateBean(beanName, mbd, instanceWrapper);
            if (exposedObject != null) {
                exposedObject = initializeBean(beanName, exposedObject, mbd);
            }
        }
        return exposedObject;
}
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
        // Here's where pvs were injected into BeanDefinition when the configuration file was parsed to get BeanDefinition
        PropertyValues pvs = mbd.getPropertyValues();
        // Delete some code and eventually call reflection assignment, which is a bit complicated to jump around.
        applyPropertyValues(beanName, mbd, bw, pvs);
}
protected void addSingleton(String beanName, Object singletonObject) {
        //It will be saved after the bean is finally created (guess, after the Spring container is initialized, the non-lazy loaded beans are already in the following way
        //Save it in the Spring container, and then get it through the @Autowired annotation. It's just the analysis, not the source code.
        synchronized (this.singletonObjects) {
            this.singletonObjects.put(beanName, 
                                      (singletonObject != null ? singletonObject : NULL_OBJECT));
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
}

Posted by silas101 on Sun, 06 Oct 2019 22:05:58 -0700