天天看點

spring源碼之-import标簽解析

1.Import标簽示例

       在SpringBoot之前,Spring配置檔案的小夥伴都知道,配置檔案的維護會讓人覺得恐怖,如果配置檔案太多,所有配置都放在一個 spring.xml 配置檔案中,那麼這個檔案就會變得很臃腫,所有針對這種情況 ,Spring 提供了一個分子產品的思路,利用 import 标簽,可以将各個不同子產品,不同作用的配置檔案導入到項目中來,這樣也大大簡化了配置後期維護的複雜度,同時也易于管理。如下圖所示:

spring源碼之-import标簽解析

2.import标簽的解析

DefaultBeanDefinitionDocumentReader:
spring源碼之-import标簽解析

       如上圖所示,為Spring解析各個預設标簽的代碼,下面就以分析importBeanDefinitionResource()這個方法來解釋Spring是怎麼解析import标簽的:

       解析 import 标簽的過程較為清晰,整個過程如下:

  • 擷取 source 屬性的值,并判斷該路徑上是絕對路徑還是相對路徑
  • 如果該路徑是絕對路徑,則調遞歸調用 Bean 的解析過程,進行另一次的解析。詳細可進入代碼getReaderContext().getReader().loadBeanDefinitions()進行追蹤,
  • 如果是相對路徑,則先計算出絕對路徑得到 Resource,然後進行解析。詳細解析
// DefaultBeanDefinitionDocumentReader:
protected void importBeanDefinitionResource(Element ele) {
		//RESOURCE_ATTRIBUTE =resource
		//1. 擷取 resource 的屬性值
		String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
		if (!StringUtils.hasText(location)) {
			getReaderContext().error("Resource location must not be empty", ele);
			return;
		}
		location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
		//實際存在的比對的資源個數
		Set<Resource> actualResources = new LinkedHashSet<>(4);
		//2.判斷路徑是相對路徑還是絕對路徑
		boolean absoluteLocation = false;
		try {
			absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
		}
		catch (URISyntaxException ex) {
		}
		// 絕對路徑
		if (absoluteLocation) {
			try {
				// 添加配置檔案位址的 Resource 到 actualResources 中,并加載相應的 BeanDefinition 們
				int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
				if (logger.isDebugEnabled()) {
					logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
				}
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error(
						"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
			}
		}
		else {
			//相對路徑,将相對路徑轉為絕對路徑然後再進行解析
			// No URL -> considering resource location as relative to the current file.
			try {
				int importCount;
				Resource relativeResource = getReaderContext().getResource().createRelative(location);
				if (relativeResource.exists()) {
					importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
					actualResources.add(relativeResource);
				}
				else {
					String baseLocation = getReaderContext().getResource().getURL().toString();
					importCount = getReaderContext().getReader().loadBeanDefinitions(
							StringUtils.applyRelativePath(baseLocation, location), actualResources);
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
				}
			}
			catch (IOException ex) {
				getReaderContext().error("Failed to resolve current resource location", ele, ex);
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
						ele, ex);
			}
		}
		//解析成功後,進行監聽器激活處理
		Resource[] actResArray = actualResources.toArray(new Resource[0]);
		getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
	}
           

3.小結

       Spring解析import标簽過程較為簡單,也比較容易了解,主要是擷取import标簽的resource屬性值,通過資源路徑,轉為Spring對資源的統一定義形式,然後調用XmlBeanDefinitionReader#loadBeanDefinitions(Resource… resources) 方法,進行遞歸的 BeanDefinition 加載,