在梳理完dubbo spi 机制后【dubbo源码系列1——spi源码解读(上)dubbo源码系列2——spi源码解读(下)】
本节开始梳理dubbo provider的启动流程,因基于dubbo源码进行研究,因此直接采用dubbo源码中dubbo-demo模块中xml配置示例作为demo。
本节将解析dubbo是如何利用spring进行自定义标签解析并注册到容器中,本节安排如下:
demo解析流程分析总结 一、demo
接口
public interface DemoService { String sayHello(String name); default CompletableFuture
接口实现
public class DemoServiceImpl implements DemoService { private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class); @Override public String sayHello(String name) { logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress(); } @Override public CompletableFuture
启动
public class ApplicationP { public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-provider.xml"); context.start(); }}
配置文件
dubbo.xsd
位于meta-INF下面,用于校验规范dubbo标签的编写;spring.schemas
http://dubbo.apache.org/schema/dubbo/dubbo.xsd=meta-INF/dubbo.xsdhttp://code.alibabatech.com/schema/dubbo/dubbo.xsd=meta-INF/compat/dubbo.xsd
xsd规范链接与实际xsd文件存放位置映射关系
spring.handlers
http://dubbo.apache.org/schema/dubbo=org.apache.dubbo.config.spring.schema.DubbonamespaceHandlerhttp://code.alibabatech.com/schema/dubbo=org.apache.dubbo.config.spring.schema.DubbonamespaceHandler
标签命名空间与实际解析handler映射关系
2、流程分析->AbstractApplicationContext#refresh ->AbstractApplicationContext#obtainFreshBeanFactory->AbstractRefreshableApplicationContext#refreshBeanFactory->AbstractXmlApplicationContext#loadBeanDefinitions->AbstractXmlApplicationContext#loadBeanDefinitions->AbstractBeanDefinitionReader#loadBeanDefinitions->XmlBeanDefinitionReader#loadBeanDefinitions->XmlBeanDefinitionReader#doLoadBeanDefinitions->XmlBeanDefinitionReader#registerBeanDefinitions->DefaultBeanDefinitiondocumentReader#registerBeanDefinitions->DefaultBeanDefinitiondocumentReader#parseBeanDefinitions
spring加载xml并解析成beanDefinition流程比较长,本节重点关注spring如何解析自定义标签解生成beanDefintion并注册到容器。
DefaultBeanDefinitiondocumentReader#parseBeanDefinitions
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;//判断节点的命名空间是否为默认的beans,如果不是则走自定义解析if (delegate.isDefaultNamespace(ele)) {parseDefaultElement(ele, delegate);}else {delegate.parseCustomElement(ele);}}}}else {delegate.parseCustomElement(root);}}
比如dubbo:application节点如下:
BeanDefinitionParserDelegate#parseCustomElement
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { //获取节点的命名空间String namespaceUri = getNamespaceURI(ele);if (namespaceUri == null) {return null;} //this.readerContext.getNamespaceHandlerResolver()为DefaultNamespaceHandlerResolverNamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);if (handler == null) {error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);return null;}return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));}
DefaultNamespaceHandlerResolver#resolve
public NamespaceHandler resolve(String namespaceUri) { //加载classpath下面所有的meta-INF/spring.handlers文件得到命名空间与handler的映射,如下截图所示Map
DefaultNamespaceHandlerResolver#getHandlerMappings
private Map
可以命名空间为http://dubbo.apache.org/schema/dubbo的标签应该用DubboNamespaceHandler解析;
DubboNamespaceHandler#init
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("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true)); registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(metadataReportConfig.class, true)); registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true)); registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.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, true)); registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser()); }
DubboNamespaceHandler#parse
public BeanDefinition parse(Element element, ParserContext parserContext) { //获取注册接口 BeanDefinitionRegistry registry = parserContext.getRegistry(); registerAnnotationConfigProcessors(registry); //注册beanProcessor,比如ReferenceAnnotationBeanPostProcessor registerCommonBeans(registry); //调用DubboBeanDefinitionParser的parse方法进行解析 BeanDefinition beanDefinition = super.parse(element, parserContext); setSource(beanDefinition); return beanDefinition; }
DubboBeanDefinitionParser#parse
private static RootBeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) { RootBeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setBeanClass(beanClass); beanDefinition.setLazyInit(false); //优先使用id,次优使用name,最后使用interface.如果包含相同的beanName,则通过增加后缀id来区分 String id = resolveAttribute(element, "id", parserContext); if (StringUtils.isEmpty(id) && required) { String generatedBeanName = resolveAttribute(element, "name", parserContext); if (StringUtils.isEmpty(generatedBeanName)) { if (ProtocolConfig.class.equals(beanClass)) { generatedBeanName = "dubbo"; } else { generatedBeanName = resolveAttribute(element, "interface", parserContext); } } if (StringUtils.isEmpty(generatedBeanName)) { generatedBeanName = beanClass.getName(); } id = generatedBeanName; int counter = 2; while (parserContext.getRegistry().containsBeanDefinition(id)) { id = generatedBeanName + (counter++); } } if (StringUtils.isNotEmpty(id)) { if (parserContext.getRegistry().containsBeanDefinition(id)) { throw new IllegalStateException("Duplicate spring bean id " + id); } //手动注册 parserContext.getRegistry().registerBeanDefinition(id, beanDefinition); beanDefinition.getPropertyValues().addPropertyValue("id", id); } //省略部分非关键代码 }
可以看到通过parserContext.getRegistry().registerBeanDefinition(id, beanDefinition)将自定义schema标签解析成beanDefinition注册到beanDefinitionMap中,解析完如下所示:
通过自定义命名空间找到自定义NamespaceHandler,通过自定义handler解析自定义标签生成beanDefinition注册到容器中。