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:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值