preface
In the last section, we got the Dubbo project off the ground with an example of integration with Spring. But how does project Dubbo work? Where is its entrance?
Dubbo uses a full Spring configuration mode to transparently access applications without any API intrusion. Spring is the only way to load Dubbo’s configuration. Dubbo is based on Spring’s Schema extension.
Initialization of Spring
1. Parse the configuration file
Do you remember what the Spring initialization process looks like? If you don’t know, you can read my previous article: Spring source Code Analysis (a) Spring initialization and XML parsing
- Loading a Configuration File
- Encapsulate it into a Resource object and parse it into a Document object
- Based on the Document node information, encapsulate the Bean object and register it
To encapsulate the Bean information, parseBeanDefinitions is called to process the node information in the configuration file.
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else{ delegate.parseCustomElement(ele); }}}}else{ delegate.parseCustomElement(root); }}}Copy the code
2. Find the namespace
In the above method, most of the time go to parseCustomElement(ele); It obtains the NamespaceHandler by retrieving namespaceUri from the configuration file, and then calls its parse method to complete the Bean registration.
public class BeanDefinitionParserDelegate { public BeanDefinition parseCustomElement(Element ele, BeanDefinition container bd) {// In Dubbo, NamespaceUri is http://dubbo.apache.org/schema/dubbo String namespaceUri = getNamespaceURI (ele); / / based on the class of the namespace for NamespaceHandler handler = this. ReaderContext. GetNamespaceHandlerResolver () resolve (namespaceUri);if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
returnnull; } // Call the method to finish loading and registering the Beanreturnhandler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }}Copy the code
So, the key question becomes: how to identify a NamespaceHandler?
Handlers/Met.inf/Spring. handlers/Map
handlerMappings. There is a file like this. Content as follows:
http://dubbo.apache.org/schema/dubbo=
com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
http://code.alibabatech.com/schema/dubbo=
com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
Copy the code
Obviously, namespaceUri for http://dubbo.apache.org/schema/dubbo will correspond to the com. Alibaba. Dubbo. Config. Spring. Schema. DubboNamespaceHandler processing class.
Spring then instantiates the DubboNamespaceHandler object through reflection, calling its initialization method init().
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
Copy the code
After initialization, the NamespaceHandler object is returned and parse() is called to process the Bean.
3, DubboNamespaceHandler
In the initialization method of DubboNamespaceHandler, Dubbo for each node to register the same handler class DubboBeanDefinitionParser, but need to pay attention to their beanClass is different.
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
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()); }}Copy the code
So, when the handler.parse() method is called, the parent class’s method is actually called. In the parent class method, find the corresponding processing class by configuring the name of the file node and actually call parse. So in Dubbo, most of the calls to is DubboBeanDefinitionParser. The parse ()
public abstract class NamespaceHandlerSupport implements NamespaceHandler { public BeanDefinition parse(Element element, ParserContext parserContext) {returnfindParserForElement(element, parserContext).parse(element, parserContext); } private BeanDefinitionParser findParserForElement(Element element, ParserContext ParserContext) {// Get the node name String in the configuration filelocalName = parserContext.getDelegate().getLocalName(element); // Find the corresponding processing class return BeanDefinitionParser parser = this.parsers.get(localName);
returnparser; }}Copy the code
After watching this process, we go back to the official website quoted at the beginning of the sentence, it has been understood. Dubbo is loaded in this way.
Load the Bean
We saw above that Spring has scanned the Configuration file for Dubbo, and now it’s time to parse and build the BeanDefinition.
This code is long because it handles all nodes in one class and relies on beanClass to determine which node is being processed, so it is full of IE Else judgments.
It encapsulates every label in the configuration file into a BeanDefinition object, processes the properties and methods of that object, registers them in the container, and waits for Spring to iterate over them on instantiation.
Take the producer-side configuration files from the previous section as an example. After they are parsed, they are encapsulated as BeanDefinition objects and put into the beanFactory.
dubbo_producer1=Root bean: class [com.alibaba.dubbo.config.ApplicationConfig]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null,
dubbo=Root bean: class [com.alibaba.dubbo.config.ProtocolConfig]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null,
com.alibaba.dubbo.config.RegistryConfig=Root bean: class [com.alibaba.dubbo.config.RegistryConfig]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null,
com.viewscenes.netsupervisor.service.InfoUserService=Root bean: class [com.alibaba.dubbo.config.spring.ServiceBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null,
infoUserService=Generic bean: class [com.viewscenes.netsupervisor.service.impl.InfoUserServiceImpl]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [dubbo_provider1.xml]
Copy the code
Instantiation of beans
After reading the information from the configuration file in the previous step and wrapping it into BeanDefinition objects, Spring loops through these beans, instantiating and dependency injection them. For details on this piece of knowledge, please refer to my previous article: Spring Source Analysis (ii) Bean instantiation and IOC dependency injection
Going back to Dubbo, each node in the configuration file corresponds to a processing class.
dubbo:application > ApplicationConfig.class
dubbo:registry > RegistryConfig.class
dubbo:protocol > ProtocolConfig.class
dubbo:service > ServiceBean.class
Copy the code
Obviously, the methods of these classes will be called during Spring’s instantiation and dependency injection. In these business methods, Dubbo activates parts of the entire framework.
Take Dubbo: Service as an example. Its counterpart is ServiceBean.class, which implements different interfaces in Spring by calling methods at different times in the Spring Bean, and finally completes dubbo’s service exposure.
package com.alibaba.dubbo.config.spring;
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean,
DisposableBean, ApplicationContextAware,
ApplicationListener<ContextRefreshedEvent>, BeanNameAware {
public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; / /... } public void onApplicationEvent(ContextRefreshedEvent event) {// Service exposureexport(a); } public void afterPropertiesSet() throws Exception {// Class initialization method //...... }}Copy the code
The other configuration nodes are the same: Spring calls the Dubbo code when instantiating the Bean to do their job.