SpringContainer
dubbo的启动过程即是Dubbo容器(Container)的启动过程。 我们通常在spring配置文件中来对dubbo的各项进行配置,dubbo启动时,SpringContainer会对spring配置文件中的dubbo配置部分进行解析, 生成各种各样的配置bean。
public class SpringContainer implements Container { public static final String SPRING_CONFIG = "dubbo.spring.config"; public static final String DEFAULT_SPRING_CONFIG = "classpath*:META-INF/spring/*.xml"; public void start() { String configPath = ConfigUtils.getProperty(SPRING_CONFIG); if (configPath == null || configPath.length() == 0) { configPath = DEFAULT_SPRING_CONFIG; } context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+")); context.start(); } }
可以看到,SpringContainer启动时,会去找dubbo.spring.config的配置,即spring配置文件的路径,如果没配置,则用默认路径classpath:META-INF/spring/.xml, 并启动spring上下文。
dubbo扩展Spring xml schema
dubbo在spring文件中的配置是利用spring的可扩展xml来实现的,具体用法在spring官方文档中有更为详细的介绍。https://docs.spring.io/spring/docs/current/spring-framework-reference/html/xml-custom.html
DubboNamespaceHandler
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 DubboBeanDefinitionParser(AnnotationBean.class, true)); } }
DubboNamespaceHandler类实现了Spring的NamespaceHandlerSupport接口,此接口只有一个方法init(),方法中为spring的命名空间注册解析器。
例如:当spring解析到
DubboBeanDefinitionParser
public class DubboBeanDefinitionParser implements BeanDefinitionParser { private final boolean required; public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) { this.beanClass = beanClass; this.required = required; } public BeanDefinition parse(Element element, ParserContext parserContext) { return parse(element, parserContext, beanClass, required); } }
DubboBeanDefinitionParser实现了BeanDefinitionParser接口,其中的parse方法提供了具体的解析方法,并生成bean交由Spring容器进行统一管理
Dubbo具体解析配置文件流程
在阅读parse方法的过程中,我将解析的流程大体分为了四个部分:
- 获取beanDefinition ID
- 注册bean
- 具体config的不同细节解析
- 属性注入
获取beanDefinition ID
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"; //protocol默认id为dubbo协议 } else { generatedBeanName = element.getAttribute("interface"); //其他config取默认interface(主要针对serviceBean) } } if (generatedBeanName == null || generatedBeanName.length() == 0) { generatedBeanName = beanClass.getName(); //还为空就取config的全路径类名作为bean的Id } id = generatedBeanName; //如果有多个,id后加递增数字以区分 int counter = 2; while(parserContext.getRegistry().containsBeanDefinition(id)) { id = generatedBeanName + (counter ++); } }
此部分是为了为BeanDifinition设置ID。具体流程如下:
- 取配置文件中的id属性,如果为空,往下走
- 如果beanClass为ProtocolConfig,则id=”dubbo”;否则取interface属性。如果为空,往下走
- id = beanClass的全路径名
- 如果Spring已经有此id的bean了,则在当前id后加递增的数字以区分。
注册bean
if (id != null && id.length() > 0) { if (parserContext.getRegistry().containsBeanDefinition(id)) { throw new IllegalStateException("Duplicate spring bean id " + id); } parserContext.getRegistry().registerBeanDefinition(id, beanDefinition); //注册bean beanDefinition.getPropertyValues().addPropertyValue("id", id); }
此步骤直接将beanDifinition向spring容器中注册并添加ID属性。
具体config的不同细节解析
if (ProtocolConfig.class.equals(beanClass)) { //循环之前加载的bean,如果有ProtocolConfig的属性时,添加 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)); } } } } 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")); } } else if (ProviderConfig.class.equals(beanClass)) { parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition); } else if (ConsumerConfig.class.equals(beanClass)) { parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition); }
此部分针对不同的配置项,进行一些不同的细节处理。
- 如果为ProtocolConfig,则为之前已经注册的且有ProtocolConfig属性的Bean进行赋值
- 如果为ServiceBean,则以”class”属性(即实现类)的类注册一个BeanDefinition
- 如果为ProviderConfig或是ConsumerConfig,则进入parseNested解析嵌套的xml配置
属性注入
剩下部分的代码都是为注册的bean设置属性的过程,细节很多,代码也很长。但是逻辑基本都很简单。后续看代码的过程如果有特殊的属性需要记录的,在此记录!