Spring 自动装配[1.2]processImport中的处理

这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战

前言

上一篇文章中我们通过idea引用查找的功能,找到分析了:

  • @Import注解是在哪里被处理的

这里我们接着往下,来看看@Import最终具体执行的三种情况:

  • DeferImportSelector
  • ImportBeanDefinitionRegistrar
  • 可以按照**@Configuration**处理的

这些情况下分别是如何处理的。

DeferImportSelector

上一篇文章里我们知道了:

  • DeferImportSelector在processImport方法中的处理方式,暂时认为只是暂时保存到deferredImportSelectors这个list中保存起来了。

那么,这些保存起来的信息具体是在哪里使用的?

处理

这个类中有一个process方法,那么就应该是处理的方法了,我们来看看代码:

public void process() {
   List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
   this.deferredImportSelectors = null;
   try {
      if (deferredImports != null) {
         DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
         deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
          //这里的register其实只是把数据往里面塞的动作
         deferredImports.forEach(handler::register);
          //看下面
         handler.processGroupImports();
      }
   }
   finally {
      this.deferredImportSelectors = new ArrayList<>();
   }
}

		public void processGroupImports() {
			for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
				Predicate<String> exclusionFilter = grouping.getCandidateFilter();
				grouping.getImports().forEach(entry -> {
					ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
					try {
                        //是不是看到了我们的老朋友?
						processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
								Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
								exclusionFilter, false);
					}
					catch (BeanDefinitionStoreException ex) {
						throw ex;
					}
					catch (Throwable ex) {
						throw new BeanDefinitionStoreException(
								"Failed to process import candidates for configuration class [" +
										configurationClass.getMetadata().getClassName() + "]", ex);
					}
				});
			}
		}
复制代码

这里就看到,又调到processImports里了。

之前的handler的方法我们没有细究,这里再贴一下代码,这样子运转机制就清晰了:

public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
   DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
   if (this.deferredImportSelectors == null) {
      DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
      handler.register(holder);
      handler.processGroupImports();
   }
   else {
      this.deferredImportSelectors.add(holder);
   }
}
复制代码

其实这里的processGroupImport,在处理import注解的Defer的时候已经调用了(processImport),那么此处综合起来看就是

  • 处理的时候,每当有一个新的Defer进来,先处理之前的存的,再把这个defer的存起来。

  • 最后调到这个process的时候,就会把所有的group执行一下,并把这些收集的group回收掉。

这个关系如下:

graph LR

processImport-->新的defer来了-->如果容器为空-->注册holder并执行-->processImport
新的defer来了-->如果容器不为空-->存起来
process-->将容器置为null-->调用processGroupImport-->注册所有holder并执行-->新建容器

而这个容器(deferredImportSelectors),只在process方法的时候会置空,处理完之后就变成了新的容器(new)。

这说明:

  • 这里的defer延迟,意即:
    • 如果process方法不在执行中时,这些importSelector会暂时保存起来。
    • 如果process方法在执行并且已经开始了(标志是:容器为空),那么此时不能偷懒往后延迟了,只能自己来执行。

DeferredImportSelectorHandler的handle方法上的注解很明确地说了这一点:

Handle the specified {@link DeferredImportSelector}. If deferred import
selectors are being collected, this registers this instance to the list. If
they are being processed, the {@link DeferredImportSelector} is also processed
immediately according to its {@link DeferredImportSelector.Group}.
复制代码

调用

代码中我们可以发现:

  • 只有通过handler,才能调用和保存这些DeferredImportSelector

而这个类只在ConfigurationClassParser.parseprocessImports中被调用:

  • parse调用process方法
  • processimport调用handle方法

小结

这里也能看出来,DeferredImportSelector也并没有实际上将import落地处理掉,处理的时候也是得委托到下面两个来。

ImportBeanDefinitionRegistrar

在processImport中,这部分的处理其实也比较简单:

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

实际上,就是实例化该类,并放到configClass中。那么这部分最终在哪里处理呢?

借助idea可以看到在这里被用到了(类:ConfigurationClassBeanDefinitionReader):

//这个方法在loadBeanDefinitions被循环调用	
private void loadBeanDefinitionsForConfigurationClass(
			ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
        //........
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

//一般来说,这里的registry实际上是在Spring启动中的容器(各种context)
	private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
		registrars.forEach((registrar, metadata) ->
				registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
	}

//而上面的这里:
	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
			BeanNameGenerator importBeanNameGenerator) {

		registerBeanDefinitions(importingClassMetadata, registry);
	}

	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //子类实现
	}
复制代码

看到registerBeanDefinitions,这里就比较清楚了:

  • 解析到的ImportBeanDefinitionRegistrar,最终会被丢到context中来初始化成beanDefinition。

这个接口的注解也说明了这一点:

Register bean definitions as necessary based on the given annotation metadata of
the importing {@code @Configuration} class.
复制代码

如果要真的看明白这个方法是如何实现的,那么就需要trace到上游调用和子类实现了,因此我们放到后面再说。【ImportBeanDefinitionRegistrar

@Configuration方式

这里的处理在这里:

processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);

	protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			return;
		}

		ConfigurationClass existingClass = this.configurationClasses.get(configClass);
        //如果这里已经保存了
		if (existingClass != null) {
            //并且想放进去的configClass,importedBy容器中不是空的
			if (configClass.isImported()) {
                //原来已有的也是通过@Import注册的(importedBy容器中不是空的)
				if (existingClass.isImported()) {
                    //我们把原来的合并一下
					existingClass.mergeImportedBy(configClass);
				}
				// Otherwise ignore new imported config class; existing non-imported class overrides it.
				return;
			}
			else {
                //这里的else对应的是上面的configClass.isImported(),意思就是:
                //我们期望放进来的,不是通过import注解的
                //看下面的注释,意思就是:把原来的记录给删掉,已现在的为准
                //但是我们是通过@Import注解进来的,这个是咋会不是import的呢?
				// Explicit bean definition found, probably replacing an import.
				// Let's remove the old one and go with the new one.
				this.configurationClasses.remove(configClass);
				this.knownSuperclasses.values().removeIf(configClass::equals);
			}
		}

        //递归地构建这个sourceClass
		// Recursively process the configuration class and its superclass hierarchy.
		SourceClass sourceClass = asSourceClass(configClass, filter);
		do {
            //这里是processImport调用的一个地方,在前面我们提到过,到这里就变成递归调用了
			sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
		}
		while (sourceClass != null);

		this.configurationClasses.put(configClass, configClass);
	}
复制代码

这里也能看到:

  • 如果是@Configuration,其实最终还是得变成ImportBeanDefinitionRegistrar,否则就继续递归了。

小结

看完这部分,应该对processImport中的四种方式有了一个大概了解:

  • 如果是要最终解析的,那么八成是得通过ImportBeanDefinitionRegistrar来导入了,否则就是一直递归到最下面,变成ImportBeanDefinitionRegistrar

  • 这里埋得坑:

    • 类:【ImportBeanDefinitionRegistrar
      • 何处处理的?
      • 子类如何实现的?
      • configurationClass类中,loadBeanDefinitionsForConfigurationClass在哪里,何时被调用?
    • 方法:processConfigurationClass
      • 这个方法中为什么会有ConfigurationClass不是import的情况出现呢,也就是说:
        • ConfigurationClass中的importedBy是如何维护的?
    • 还有之前没有处理的问题:
      • doProcessConfigurationClass干了啥?

猜你喜欢

转载自juejin.im/post/7032216686086324232
今日推荐