自动装配作为SpringBoot四大核心(启动依赖、自动装配、Actuator、命令行界面)组件中最常用之一,重要性不言而喻。因涉及内容比较多,因此准备通过上下两节深入源码探究竟底层如何实现自动装配,通过阅读本篇以及下一篇可以清楚下面三个问题:

  • 如何实现自动?
  • 装配什么?
  • 如何实现自定义starter?

      因SpringBoot构建在Spring框架上,因此有必要先重点分析Spring中@Import注解,因此本节整体安排如下:

  1. @Import注解
  2. demo代码
  3. 源码分析
  4. 总结

      本节重点解读@Import注解以及处理流程。

1、@Import

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
     /**
	 * {@link Configuration @Configuration}, 
              * {@link ImportSelector},
	 * {@link ImportBeanDefinitionRegistrar}, 
              or regular component classes to import.
	 */
	Class<?>[] value();
}

      从注解的注释可以看到@Import导入的类型可以分成以下三种;

类型

备注

普通类

将@Import导入的类定义注册到beanDefinitionMap

ImportSelector

将selectImports方法返回类所对应的类定义注册到beanDefinitionMap

ImportBeanDefinitionRegistrar

手动生成beanDefinition注册到容器beanDefinitionMap

2、demo

  • 三个需要导入的原始类
public class ImportBean {
}

public class ImportSelectBean {
}

public class RegistrarBean {
}
  • ImportSelector 实现类
public class ImportSelectorImpl implements ImportSelector {
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		return new String[]{"xu.jiang.hua.im.ImportSelectBean"};
	}
}
  • ImportBeanDefinitionRegistrar实现类
public class ImportBeanDefinitionRegistrarImpl implements ImportBeanDefinitionRegistrar {
	
             @Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
	    //bean定义
		GenericBeanDefinition bd = new GenericBeanDefinition();
		bd.setBeanClass(RegistrarBean.class);
		registry.registerBeanDefinition("registrarBean",bd);
	}
}
  • 导入配置类
//分别导入ImportBean
@Configuration
@Import({ImportBean.class, ImportSelectorImpl.class, ImportBeanDefinitionRegistrarImpl.class,})
public class Config {
}

3、源码分析

  • doProcessConfigurationClass

    接下来重点看看spring如何处理@Import注解,
.......
 processImports(configClass, sourceClass, getImports(sourceClass), true);
 .....

      比如@EnableAutoConfiguration作为复合注解,里面包含@Import(AutoConfigurationImportSelector.class),因此getImports需要通过递归的方式获取到当前类注解上所导入的所有类,具体代码如下:

  • getImports
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
		Set<SourceClass> imports = new LinkedHashSet<>();
		Set<SourceClass> visited = new LinkedHashSet<>();用于记录已经访问过的sourceClass
		collectImports(sourceClass, imports, visited);//通过sourceClass导入的所有类

		return imports;
	}

private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited){
		if (visited.add(sourceClass)) {
        //找出当前类上的所有注解,然后遍历注解。对每个注解进行递归,直到无注解或者是@Import			for (SourceClass annotation : sourceClass.getAnnotations()) {
				String annName = annotation.getMetadata().getClassName();
				if (!annName.equals(Import.class.getName())) {
					collectImports(annotation, imports, visited);
				}
			}
                     imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
		}
	}
  • processImports
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

		if (importCandidates.isEmpty()) {
			return;
		}

		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
		 //省略非关键代码
		}
		else {
			this.importStack.push(configClass);
			try {
				//遍历刚才找出的import类
				for (SourceClass candidate : importCandidates) {
					if (candidate.isAssignable(ImportSelector.class)) {
						// Candidate class is an ImportSelector -> delegate to it to determine imports
						Class<?> candidateClass = candidate.loadClass();
						//实例化selector
						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 {
							//回调selector方法
							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
							//导入类上可能还存在@Import注解 因此继续递归
							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);
						//将导入注册类实例化后并调用各种aware接口,并注册到importBeanDefinitionRegistrars
						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());
						//生成新的configurationClass,并标记是由哪个类导入的
						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();
			}
		}
	}

整体流程如下图所示

springboot启动项目后target中没有java文件_自动装配

  1. 判断导入类是否实现ImportSelector。如果是,则跳转第2步,否则跳转第4步;
  2. 实例化ImportSelector,继续判断是否实现DeferredImportSelector(DeferredImportSelector继承ImportSelector接口)。如果是,则加入DeferredImportSelectorHandler中的deferredImportSelectors中(此处与自动装配有关!!)。如果不是,则跳转第3步;
  3. 回调ImportSelector类的selectImports,得到需要导入的类,将此作为导入类继续调用processImports。
  4. 判断导入类是否实现ImportBeanDefinitionRegistrar,如果是则实例化,然后注册到 importBeanDefinitionRegistrars(key:ImportBeanDefinitionRegistrar,value:AnnotationMetadata)。如果不是,则跳转第5步;
  5. 将导入类生成ConfigurationClass类,并标记当前导入类由configClass导入,调用processConfigurationClass;
  • 执行完doProcessConfigurationClass后configurationClasses

springboot启动项目后target中没有java文件_spring_02

      从上图中方框1、方框2、方框3和方框4可以看到ImportBean、ImportSelectBean通过ConfigurationClass表示并通过importBy属性表示被哪个类(此处为Config)导入,但此时还未将导入的类注册到beanDefinitionMap,接下来执行loadBeanDefinitions回调方法将类定义注册到beanDefinitionMap,上面也可以下图进行表示:

springboot启动项目后target中没有java文件_spring boot_03

  • loadBeanDefinitions
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
         //省略非核心关键代码
         parser.parse(candidates);
		 parser.validate();

			Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
			configClasses.removeAll(alreadyParsed);

			// Read the model and create bean definitions based on its content
			if (this.reader == null) {
				this.reader = new ConfigurationClassBeanDefinitionReader(
						registry, this.sourceExtractor, this.resourceLoader, this.environment,
						this.importBeanNameGenerator, parser.getImportRegistry());
			}
			//处理@Import标注的类以及方法上有@Bean标记的类(前面只是将相应的类找出来,并未注册到beanDefinitionMap中)
	this.reader.loadBeanDefinitions(configClasses);
	//省略非核心关键代码
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
		TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
		//遍历所有的Configurations
		for (ConfigurationClass configClass : configurationModel) {
			loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
		}
	}

      接下来看loadBeanDefinitionsForConfigurationClass方法:

private void loadBeanDefinitionsForConfigurationClass(
			ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator){
	      //省略非关键代码
        //如果类通过import导入,此时实现注册到beanDefinitionMap,比如ImportBean ImportSelectBean
		if (configClass.isImported()) {
			registerBeanDefinitionForImportedConfigurationClass(configClass);
		}
		//将@Bean标记的方法注册到beanDefinitionMap
		for (BeanMethod beanMethod : configClass.getBeanMethods()) {
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}

		//ImportResources
         loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());

		//回调BeanDefinitionRegistrar的实现类,比如RegistrarBean就通过此处实现注入
       loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
	}

      可以看到loadBeanDefinitions不仅处理@Import导入的类,同时处理@Bean和@ImportResources注解,此处重点关注@Import的处理。

  • registerBeanDefinitionForImportedConfigurationClass
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
		AnnotationMetadata metadata = configClass.getMetadata();
		AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);

		//解析是否生成代理类,打标操作
		ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
		configBeanDef.setScope(scopeMetadata.getScopeName());
		//生成beanName
		String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
		//解析该类的 @Lazy、@Primary、@DependsOn、@Role等属性 并设置到beanDefinition中
		AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);

		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
        //生成代理类的definitionHolder
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
        //注册到beanDefinitionMap
		this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
		//回设beanName
		configClass.setBeanName(configBeanName);

		if (logger.isTraceEnabled()) {
			logger.trace("Registered bean definition for imported class '" + configBeanName + "'");
		}
	}

      通过传进来的configClass生成BeanDefinitionHolder通过this.registry.registerBeanDefinition方法手动注册到beanDefinitionMap中,接下来看看BeanDefinitionRegistrar回调处理

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

4、总结

      通过简单demo对@Import完成分析,总结如下:

  1. 针对通过ImportSelect导入的类以及普通类,执行完processConfigurationClass后,会根据导入类生成ConfigurationClass放入private final Map<ConfigurationClass, ConfigurationClass> configurationClasses = new LinkedHashMap<>(),但是都还没注册beanDefinitionMap中,通过后续的this.reader.loadBeanDefinitions方法实现注册到 beanDefinitionMap;
  2. 通过ImportBeanDefinitionRegistrar接口手动注册的类,未放入configurationClasses中,将接口实现类标记在ConfigurationClass中的
    importBeanDefinitionRegistrars属性上,最终通过this.reader.loadBeanDefinitions方法实现注册到
    beanDefinitionMap;
    通过本节对@Import注解的梳理,下节梳理自动装配将会变得容易。