天天看点

3.1 dubbo-加载配置文件

1 自定义标签

遗留问题:

  • 在AbstractApplicationContext 中的prepareBeanFactory方法设置了addBeanPostProcessor ,是否和Bean 中的生命周期有关。在ClassPathXmlApplicationContext中调用了AsbstractApplicationContext的refresh方法。
  • 依据RootBeanDefinition 实例化Bean

代码总结:

  • 缓存数据模式 读缓存(未命中) - 加锁 - 读缓存 (未命中)- 缓存数据
  • SPI实现依据参数读取不同的实现类。

dubbo-demo-consumer.xml 中使用了Dubbo自定义标签,例如:dubbo:application;dubbo:reference;在spring中使用自定义标签,有如下几步:

  • 设置配置属性和JavaBean

    设置一个与标签对应的JavaBean,用于存放标签中的属性值, 例如Dubbo中的ApplicationConfig、ReferenceConfig

  • 设置XSD文件

    文件内容如 xsd链接 所示

  • 编写BeanDefinitionParser

    实现对配置文件中的标签解析。如 DubboBeanDefinitionParser

  • 编写NamespaceHandler

    实现标签和解析类BeanDefinitionParser链接,如DubboNamespaceHandler,其中的init()代码如下:

// DubboNamespaceHandler.init()
    @Override
    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("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());
    }
    
    /*NamespaceHandle的init方法主要调用继承NamespaceHandlerSupport的registerBeanDefinitionParser方法.*/
	private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();
	/**
	 * Subclasses can call this to register the supplied {@link BeanDefinitionParser} to
	 * handle the specified element. The element name is the local (non-namespace qualified)
	 * name.
	 */
	protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
		this.parsers.put(elementName, parser);
		//因此parsers中保存标签和BeanDefinitionParser的映射。
           

可以看出,dubbo新建的标签主要有application,consumer、reference等,spring 在根据标签查找对应的DubboNamespaceHandler的调用位置如下图:

3.1 dubbo-加载配置文件

调用链如图:

3.1 dubbo-加载配置文件

resolve的代码如下:

@Override
	@Nullable
	public NamespaceHandler resolve(String namespaceUri) {
		Map<String, Object> handlerMappings = getHandlerMappings();
		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) BeanUtils.instantiateClass(handlerClass);
				//调用init方法,注册标签和对应的DubboBeanDefinitionParser类
				namespaceHandler.init();
				handlerMappings.put(namespaceUri, namespaceHandler);
				return namespaceHandler;
			}
			catch (ClassNotFoundException ex) {
				throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
						"] for namespace [" + namespaceUri + "]", ex);
			}
			catch (LinkageError err) {
				throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
						className + "] for namespace [" + namespaceUri + "]", err);
			}
		}
	}
           

入参namespaceUri 来源于配置文件中的标签中的内容。方法getHandlerMappings()代码如下:

/**
	 * Load the specified NamespaceHandler mappings lazily.
	 */
	private Map<String, Object> getHandlerMappings() {
		// 如果map中缓存数据,则直接去map中的数,map的格式是<namespaceUri, NamespaceHandler对象>
		Map<String, Object> handlerMappings = this.handlerMappings;
		if (handlerMappings == null) {
			synchronized (this) {
				handlerMappings = this.handlerMappings;
				if (handlerMappings == null) {
					if (logger.isTraceEnabled()) {
						logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
					}
					try {
						//默认情况下handlerMappingsLocation = "META-INF/spring.handlers", 
						Properties mappings =
								PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
						if (logger.isTraceEnabled()) {
							logger.trace("Loaded NamespaceHandler mappings: " + mappings);
						}
						handlerMappings = new ConcurrentHashMap<>(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 handlerMappings;
	}
           

开始时,handlerMappings = null, 只能通过PropertiesLoaderUtils.loadAllProperties来加载所有的"META-INF/spring.handlers" 文件,该方法的代码如下:

/**
	 * Load all properties from the specified class path resource
	 * (in ISO-8859-1 encoding), using the given class loader.
	 * <p>Merges properties if more than one resource of the same name
	 * found in the class path.
	 * @param resourceName the name of the class path resource
	 * @param classLoader the ClassLoader to use for loading
	 * (or {@code null} to use the default class loader)
	 * @return the populated Properties instance
	 * @throws IOException if loading failed
	 */
	public static Properties loadAllProperties(String resourceName, @Nullable ClassLoader classLoader) throws IOException {
		Assert.notNull(resourceName, "Resource name must not be null");
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = ClassUtils.getDefaultClassLoader();
		}
		Enumeration<URL> urls = (classLoaderToUse != null ? classLoaderToUse.getResources(resourceName) :
				ClassLoader.getSystemResources(resourceName));
		Properties props = new Properties();
		while (urls.hasMoreElements()) {
			URL url = urls.nextElement();
			URLConnection con = url.openConnection();
			ResourceUtils.useCachesIfNecessary(con);
			InputStream is = con.getInputStream();
			try {
				if (resourceName.endsWith(XML_FILE_EXTENSION)) {
					props.loadFromXML(is);
				}
				else {
					props.load(is);
				}
			}
			finally {
				is.close();
			}
		}
		return props;
	}

}
           

其中Dubbo-2.7.3.jar中的文件内容为:

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
           

查找DubboNamespaceHandler总结:加载类路径下所有的META-INF/spring.handlers文件,其中存放namespaceUri和NamespaceHandler的映射;加载并实例化对应的NamespaceHandler类,调用init方法,并存入map中,便于下次调用。

2 解析Dubbo标签

调用链如图:

3.1 dubbo-加载配置文件
//BeanDefinitionParserDelegate.parseCustomerElement方法  
	/**
	 * Parse a custom element (outside of the default namespace).
	 * @param ele the element to parse
	 * @param containingBd the containing bean definition (if any)
	 * @return the resulting bean definition
	 */
	@Nullable
	public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
		String namespaceUri = getNamespaceURI(ele);
		if (namespaceUri == null) {
			return null;
		}
		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));
	}
//DubboNamespaceHanler中的parse方法,继承自NamespaceHandlerSupport
	/**
	 * Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is
	 * registered for that {@link Element}.
	 */
	@Override
	@Nullable
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		BeanDefinitionParser parser = findParserForElement(element, parserContext);
		//详细代码见下
		return (parser != null ? parser.parse(element, parserContext) : null);
	}
	//DubboBeanDefinitionParser,parse方法
	@Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        return parse(element, parserContext, beanClass, required);
    }
           

DubboBeanDefinitionParser.parse的方法可以,一个方法就可以实现把所有的标签内容存放在RootBeanDefinition,不过生成的RootBeanDefinition结果没有保存?

3 依据BeanDefinition实例化Bean

这一段代码与Dubbo关系不大,也有相关文档解析,就不贴代码了,实例化的调用链如图所示:

3.1 dubbo-加载配置文件