Spring IOC(十二)@Import和 @PropertySource 註解研究

ConfigurationClassPostProcessor 有對多個 註解進行分析,本文主要圍繞 @Import@PropertySource 進行詳細分析:

Import

@Import 註解解析在 ConfigurationClassPostProcessor 解析中進行,具體位置在 ConfigurationClassParseprocessImports 中:

	private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
		// 判斷是否有import類
		if (importCandidates.isEmpty()) {
			return;
		}

		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
			this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
		}
		else {
			this.importStack.push(configClass);
			try {
				for (SourceClass candidate : importCandidates) {
					if (candidate.isAssignable(ImportSelector.class)) {
					// 判斷import中類型
						// Candidate class is an ImportSelector -> delegate to it to determine imports
						Class<?> candidateClass = candidate.loadClass();
						ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
						ParserStrategyUtils.invokeAwareMethods(
								selector, this.environment, this.resourceLoader, this.registry);
						if (selector instanceof DeferredImportSelector) {
							this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
						}
						else {
							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
							processImports(configClass, currentSourceClass, importSourceClasses, false);
						}
					}
					else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
						// Candidate class is an ImportBeanDefinitionRegistrar ->
						// delegate to it to register additional bean definitions
						Class<?> candidateClass = candidate.loadClass();
						ImportBeanDefinitionRegistrar registrar =
								BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
						ParserStrategyUtils.invokeAwareMethods(
								registrar, this.environment, this.resourceLoader, this.registry);
						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
					}
					else {
						// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
						// process it as an @Configuration class
						this.importStack.registerImport(
								currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
						processConfigurationClass(candidate.asConfigClass(configClass));
					}
				}
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Throwable ex) {
				throw new BeanDefinitionStoreException(
						"Failed to process import candidates for configuration class [" +
						configClass.getMetadata().getClassName() + "]", ex);
			}
			finally {
				this.importStack.pop();
			}
		}
	}

基於 @Import 引入類類型不同,Spring 容器分爲三種進行處理:

  1. 普通類,則會將其作爲一個configuration 加入到待解析容器中,進行下次迭代解析。
  2. ImportBeanDefinitionRegistrar 類,則會放入 ConfigClassimportBeanDefinitionRegistrars 中,因此父類解析完最後,再解析
  3. ImportSelector 類,則分爲兩種情況。如果是 DeferredImportSelector,則將放到最後進行解析,如果是普通的 ImportSelector則會立即調用
    selectImports 獲取候選類進行解析。

Import 普通類

對於普通類的處理,Spring的處理方式是進行遞歸調用 當前 processConfigurationClass 方法:

						this.importStack.registerImport(
								currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
						processConfigurationClass(candidate.asConfigClass(configClass));

ImportSelector 類

ImportSelector 類分爲兩種情況,其實差別挺大的。

DeferredImportSelector類

先看 DeferredImportSelector 接口結構:

public interface DeferredImportSelector extends ImportSelector {

	@Nullable
	default Class<? extends Group> getImportGroup() {
		return null;
	}

	interface Group {
		// 處理類
		void process(AnnotationMetadata metadata, DeferredImportSelector selector);
		// 獲取import操作
		Iterable<Entry> selectImports();
		class Entry {}
	}

上面接口有以下幾個用途:

  1. getImportGroup 返回一個Group對象
  2. Groupprocess 用於處理類,而 selectImports 則主要用於返回 Entry 類型的集合。

下面從這幾個方法調用來看看具體用途。
ConfigurationClassParser 中:

  1. processImports 中,如果是 DeferredImportSelector 類型,則會加入到 DeferredImportSelectorHandler 中,用於後續處理:
    DeferredImportSelectorHandlerConfigurationClassParser 內部類,主要用於緩存 DeferredImportSelector,用於最後執行。
						if (selector instanceof DeferredImportSelector) {
							this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
						}
  1. parse方法執行之後,最後一行則會執行 this.deferredImportSelectorHandler.process();
		public void process() {
			// 獲取所有的DeferredImportSelectorHolder
			List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
			this.deferredImportSelectors = null;
			try {
				if (deferredImports != null) {
					DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
					// 對 DeferredImportSelectorHolder 進行排序
					deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
					// 調用每一個 DeferredImportSelector 的register 方法
					deferredImports.forEach(handler::register);
					// 對所有從 Group 中對象,執行其 selectImports 方法。
					handler.processGroupImports();
				}
			}
			finally {
				this.deferredImportSelectors = new ArrayList<>();
			}
		}

下面看 DeferredImportSelectorGroupingHandlerregisterprocessGroupImports

		public void register(DeferredImportSelectorHolder deferredImport) {
			Class<? extends Group> group = deferredImport.getImportSelector()
					.getImportGroup();
					// 如果是 DeferredImportSelector 接口,但是 其返回import接口爲null,那麼就去執行 `selectImports` 方法
			DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
					(group != null ? group : deferredImport),
					key -> new DeferredImportSelectorGrouping(createGroup(group)));
			grouping.add(deferredImport);
			// 加入到 configurationClasses 中
			this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
					deferredImport.getConfigurationClass());
		}

		public void processGroupImports() {
			for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
			// 調用所有的group調用其getImports,而後對所有返回對象 執行 `processImports` 方法
				grouping.getImports().forEach(entry -> {
					ConfigurationClass configurationClass = this.configurationClasses.get(
							entry.getMetadata());
					try {
						processImports(configurationClass, asSourceClass(configurationClass),
								asSourceClasses(entry.getImportClassName()), false);
					}
					catch (BeanDefinitionStoreException ex) {
						throw ex;
					}
					catch (Throwable ex) {
						throw new BeanDefinitionStoreException(
								"Failed to process import candidates for configuration class [" +
										configurationClass.getMetadata().getClassName() + "]", ex);
					}
				});
			}
		}

普通 ImportSelector 類

Spring 對其解析是直接執行 selectorselectImports 方法,對返回的集合對象,在進行遞歸調用 processImports

							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
							processImports(configClass, currentSourceClass, importSourceClasses, false);

ImportBeanDefinitionRegistrar 類

addImportBeanDefinitionRegistrar 類型則會在 parse 方法執行結束後,由Spring 統一解析並執行其 registerBeanDefinitions,Spring 允許其通過該方法註冊相關bean信息。
處理 ImportBeanDefinitionRegistrar

						Class<?> candidateClass = candidate.loadClass();
						ImportBeanDefinitionRegistrar registrar =
								BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
						ParserStrategyUtils.invokeAwareMethods(
								registrar, this.environment, this.resourceLoader, this.registry);
						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());

最終在 parse 執行之後,在 ConfigurationClassBeanDefinitionReaderloadBeanDefinitionsFromRegistrars 進行:

	private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
		registrars.forEach((registrar, metadata) ->
				registrar.registerBeanDefinitions(metadata, this.registry));
	}

即執行所有 ImportBeanDefinitionRegistrar 類型的 registerBeanDefinitions 方法。

PropertySource

@PropertySource 主要將 properties 下配置文件放入到 Enviroment 中:

@PropertySource 解析在 ConfigurationClassPostProcessor 中會被解析。在 掃描並解析 ConfigurationConponent 標籤時,遇到 PropertySource 會有如下解析:
ConfigurationClassParserdoProcessConfigurationClass 中:

		// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

processPropertySource 中:

	private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
	// 獲取name
		String name = propertySource.getString("name");
		if (!StringUtils.hasLength(name)) {
			name = null;
		}
		// 獲取字節編碼
		String encoding = propertySource.getString("encoding");
		if (!StringUtils.hasLength(encoding)) {
			encoding = null;
		}
		// 獲取值
		String[] locations = propertySource.getStringArray("value");
		Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
		boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
		// 獲取解析器PropertySourceFactory
		Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
		PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
				DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));

		for (String location : locations) {
			try {
			// 將Resource 加載出來,然後調用 addPropertySource 放到environment 的  propertySources 中
				String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
				Resource resource = this.resourceLoader.getResource(resolvedLocation);
				addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
			}
			catch (IllegalArgumentException | FileNotFoundException | UnknownHostException ex) {
				// Placeholders not resolvable or resource not found when trying to open it
				if (ignoreResourceNotFound) {
					if (logger.isInfoEnabled()) {
						logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
					}
				}
				else {
					throw ex;
				}
			}
		}
	}
  1. 上面方法主要是將 解析出來 Resource,放入到 SpringContextenviromentpropertySources了。

也就是說,如果你一旦使用 @PropertySourceproperties 中定義,在全局中都可以使用,也就是容器中 類A上加註解引入了,你可以在容器類B中使用。

總結

對於 @Import 難免有點抽象,可以使用幾個例子進行分析,https://github.com/anLA7856/springLearn/

覺得博主寫的有用,不妨關注博主公衆號: 六點A君。
哈哈哈,一起研究Spring:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章