天天看點

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-加載配置檔案