Dubbo源码分析-Dubbo是如何整合spring-framework的

  这篇文章是Dubbo源码分析的开端,与其说这篇文章是Dubbo源码分析,不如是spring源码分析,因为大部分都是在分析spring如何解析xml配置文件的,为了与后面的Dubbo源码分析保持一致,姑且这样命名了。
  
  使用Dubbo框架开发分布式服务时,一般使用spring进行管理,在spring的配置文件中进行配置,例如服务提供者Provider端配置如下:
  
  <?xml version="1.0" encoding="UTF-8"?>
  
  <beans xmlns="http://www.springframework.org/schema/beans"
  
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  
  xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
  
  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="hello-world-app"  />
  
  <!-- 使用multicast广播注册中心暴露服务地址 -->
  
  <dubbo:registry address="multicast://224.5.6.7:1234" />
  
  <!-- 用dubbo协议在20880端口暴露服务 -->
  
  <dubbo:protocol name="dubbo" port="20880" />
  
  <!-- 声明需要暴露的服务接口 -->
  
  <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" />
  
  <!-- 和本地bean一样实现服务 -->
  
  <bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" />
  
  </beans>
  
  对于
  
  (省略描述....)
  
  public interface NamespaceHandler {
  
  void init();
  
  BeanDefinition parse(Element element, ParserContext parserContext);
  
  BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);
  
  }
  
  可以看出其中有一个parse方法,它的作用就是用于解析Xml文档中的节点。这个接口有一个抽象的实现类NamespaceHandlerSupport,具体的标签解析器都继承这个抽象类,我们来看下有哪些:
  
  NamespaceHandler
  
  可以看见我们熟悉的Aop还有Dubbo NamespaceHandler.那Spring是如何知道要使用Dubbo定义的handler来解析自定义的标签呢?他们的结合点就在一个配置文件,spring留了一个配置文件,只要我们配置了它,spring就可以找到。这个配置文件名字叫做spring.handlers,spring在解析xml文件时,会去加载spring.handlers配置文件,然后寻找能够解析自定义标签的handler。
  
  spring.handlers在Dubbo中是怎样的内容呢,我们一起来看下:
  
  路径在:dubbo-config/dubbon-config-spring/META-INF/spring.handlers
  
  内容如下:
  
  http://dubbo.apache.org/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
  
  http://code.alibabatech.com/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
  
  我们来看一下spring是如何找到这个文件并生成对应标签namespaceHandler的:
  
  ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"application.xml"});
  
  我们使用spring框架,启动的时候都会写这么一句话,加载xml配置文件.当执行到AbstractXmlApplicationContext类的loadBeanDefinitions方法时,会创建一个XmlBeanDefinitionReader对象,读取读取并解析xml.
  
  /**
  
  * Loads the bean definitions via an XmlBeanDefinitionReader.
  
  * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
  
  * @see #initBeanDefinitionReader
  
  * @see #loadBeanDefinitions
  
  */
  
  @Override
  
  protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
  
  // Create a new XmlBeanDefinitionReader for the given BeanFactory.
  
  XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
  
  // Configure the bean definition reader with this context's
  
  // resource loading environment.
  
  beanDefinitionReader.setEnvironment(this.getEnvironment(www.jyz521.com));
  
  beanDefinitionReader.setResourceLoader(this);
  
  beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
  
  // Allow a subclass to provide custom initialization of the reader,
  
  // then proceed with actually loading the bean definitions.
  
  initBeanDefinitionReader(beanDefinitionReader);
  
  loadBeanDefinitions(beanDefinitionReader);
  
  }
  
  最后一句话loadBeanDefinitions(beanDefinitionReader),是使用XmlBeanDefinitionReader将xml解析成BeanDefinition.
  
  接下来调用XmlBeanDefinitionReader的registerBeanDefinitions方法创建BeanDefinitionDocumentReader对象,这个对象才是真正解析XML的对象。
  
  public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  
  BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  
  int countBefore = getRegistry().getBeanDefinitionCount();
  
  //创建ReaderContext,并调用documentReader的registerBeanDefinitions方法
  
  documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  
  return getRegistry().getBeanDefinitionCount(www.leyou2.net) - countBefore;
  
  }
  
  大家再来看下createReaderContext方法
  
  /**
  
  * Create the {@link XmlReaderContext} to pass over to the document reader.
  
  */
  
  public XmlReaderContext createReaderContext(Resource resource) {
  
  return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
  
  this.sourceExtractor, this, getNamespaceHandlerResolver());
  
  }
  
  终于出现了NamespaceHandler,那就是getNamespaceHandlerResolver()方法,再来看下这个方法
  
  public NamespaceHandlerResolver getNamespaceHandlerResolver() {
  
  if (this.namespaceHandlerResolver == null) {
  
  this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
  
  }
  
  return this.namespaceHandlerResolver;
  
  }
  
  protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
  
  return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
  
  }
  
  public DefaultNamespaceHandlerResolver(ClassLoader classLoader) {
  
  this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
  
  }
  
  //最终会调用这个构造器实例化DefaultNamespaceHandlerResolver类,此时handlerMappingsLocation成员变量的值为META-INF/spring.handlers了
  
  public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) {
  
  Assert.notNull(handlerMappingsLocation, www.taohuayuan178.com"Handler mappings location must not be null");
  
  this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
  
  this.handlerMappingsLocation = handlerMappingsLocation;
  
  }
  
  /**
  
  * The location to look for the mapping files. Can be present in multiple JAR files.
  
  */
  
  public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
  
  //读取META-INF/spring.handlers配置文件的内容
  
  private Map<String, Object>www.thd178.com getHandlerMappings() {
  
  if (this.handlerMappings == null) {
  
  synchronized (this) {
  
  if (this.handlerMappings == null) {
  
  try {
  
  Properties mappings =
  
  PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
  
  if (logger.isDebugEnabled(www.bomaoyule.cn/)) {
  
  logger.debug("Loaded NamespaceHandler mappings: " + mappings);
  
  }
  
  Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
  
  CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
  
  this.handlerMappings = handlerMappings;
  
  }
  
  catch (IOException ex) {
  
  throw new IllegalStateException(
  
  "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
  
  }
  
  }
  
  }
  
  }
  
  return this.handlerMappings;
  
  }
  
  会创建一个默认的DefaultNamespaceHandlerResolver对象,其中有一个变量DEFAULT_HANDLER_MAPPINGS_LOCATION,其值是META-INF/spring.handlers,此时可以看到spring会去加载spring.hadlers中的内容。
  
  那加载完后,会在什么地方使用呢?
  
  我们来看一下DefaultBeanDefinitionDocumentReader类中的parseBeanDefinitions方法
  
  protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  
  if (delegate.isDefaultNamespace(root)) {
  
  NodeList nl = root.getChildNodes(www.mhylpt.com);
  
  for (int i = 0; i < nl.getLength(); i++) {
  
  Node node = nl.item(i);
  
  if (node instanceof Element) {
  
  Element ele = (Element) node;
  
  //判断标签是标准的标准还是自定义的,判断的依据就是标准的namespace是否是http://www.springframework.org/schema/beans
  
  //如果是,则是标准的标签,如果不是则不是标准的标签
  
  if (delegate.isDefaultNamespace(ele)) {
  
  parseDefaultElement(ele,www.feifanyule.cn/ delegate);
  
  }
  
  else {
  
  delegate.parseCustomElement(ele);
  
  }
  
  }
  
  }
  
  }
  
  else {
  
  delegate.parseCustomElement(root);
  
  }
  
  }
  
  接下来看一下BeanDefinitionParserDelegate类的parseCustomElement方法,BeanDefinitionParserDelegate对象是在DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions方法中生成的
  
  ,它的作用是用来解析标签的。
  
  public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
  
  //根据标签得到标签的namespace
  
  //dubbo自定义的标签得到的namespace是http://dubbo.apache.org/schema/dubbo
  
  String namespaceUri = getNamespaceURI(ele);
  
  NamespaceHandler 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));
  
  }
  
  接下来看下DefaultBeanDefinitionDocumentReader的resolve方法
  
  @Override
  
  public NamespaceHandler resolve(String namespaceUri) {
  
  //得到加载的内容
  
  Map<String, Object> handlerMappings = getHandlerMappings();
  
  //根据标签的namspace得到handler类名
  
  //dubbo的namespace为http://dubbo.apache.org/schema/dubbo
  
  Object handlerOrClassName = handlerMappings.get(namespaceUri);
  
  if (handlerOrClassName == null) {
  
  return null;
  
  }
  
  else if (handlerOrClassName instanceof NamespaceHandler) {
  
  return (NamespaceHandler) handlerOrClassName;
  
  }
  
  else {
  
  String className = (String) handlerOrClassName;
  
  try {
  
  Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
  
  if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
  
  throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
  
  "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
  
  }
  
  //实例化namespaceHandler
  
  NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
  
  namespaceHandler.init();
  
  handlerMappings.put(namespaceUri, namespaceHandler);
  
  return namespaceHandler;
  
  }
  
  catch (ClassNotFoundException ex) {
  
  throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
  
  namespaceUri + "] not found", ex);
  
  }
  
  catch (LinkageError err) {
  
  throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
  
  namespaceUri + "]: problem with handler class file or dependent class", err);
  
  }
  
  }
  
  }
  
  得到DubboNamespaceHandler实例后,调用其init()方法
  
  @Override
  
  public void init() {
  
  //将application、module等名称做为key注册到解析器Map中
  
  //这些key名称正是dubbo自定义标签的localName,例如<dubbo:application />标签的LocalName是application
  
  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());
  
  }
  
  可以看到所有的key(除了annotation)对应的标签解析器都是DubboBeanDefinitionParser实例对象,至于DubboBeanDefinitionParser是如何解析标签的,这里就不做分析了,如果想要了解,可以看看他的源码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值