spring源码系列5——spring启动流程3

        上一节分析invokeBeanFactoryPostProcessors方法时,仅对 parser.parse(candidates)进行深入分析。本节着重分析 this.reader.loadBeanDefinitions(configClasses)方法。

预备知识——初识@Import

@Import注解导入的类总共分成三种:

  1. 导入普通类——将导入类注册到容器中;
  2. 导入ImportSelector接口实现类——将selectImport方法返回的全路径类注册到容器中;
  3. 导入ImportBeanDefinitionRegistrar接口实现类——通过回调接口的registerBeanDefinitions方法手动将需要注册的类进行注册;

1、processImports

        上一节只针对doProcessConfigurationClass整体流程进行分析,如下图所示。先分析doProcessConfigurationClass方法中的对@Import注解的处处理。
spring源码系列5——spring启动流程3_第1张图片

processImports(configClass, sourceClass, getImports(sourceClass), true);
	private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
			throws IOException {
     
		//将当前的sourceClass进入visited 防止无限递归
		if (visited.add(sourceClass)) {
     
			//找出当前类上的所有注解,然后遍历注解,直到注解是@Import或者注解为空
			for (SourceClass annotation : sourceClass.getAnnotations()) {
     
				String annName = annotation.getMetadata().getClassName();
				//比如@Enablexx,需要递归继续调用
				if (!annName.equals(Import.class.getName())) {
     
					collectImports(annotation, imports, visited);
				}
			}
			imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
		}
	}

getImports返回当前类的所有导入类。

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
     
		if (importCandidates.isEmpty()) {
     
			return;
		}
        //Import循环校验
		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
     
			this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
		}
		else {
     
			//将当前类加入栈中
			this.importStack.push(configClass);
			try {
     
				for (SourceClass candidate : importCandidates) {
     
					/**
					 * @import导入的类总共有三种:A、普通类、实现ImportSelect、ImportBeanDefinitionRegistrar的类
					 *
					 * 1、对于实现ImportSelector接口的导入类,如果并且还为deferredImportSelectorHandler的实现类,则在自动装配时处理。
					 * 对于仅实现的ImportSelector接口的类先实例化,然后调用selectImport接口得到要导入的类,然后转化成sourceClass然后
					 * 递归调用processImports;
					 *
					 * 2、对于实现ImportBeanDefinitionRegistrar的导入,同样进行实例化,然后加入
					 * Map importBeanDefinitionRegistrars =new LinkedHashMap<>()
					 * Key为实例,value为导入该类的类信息(比如当前candidateClass是A,A是Config类导入的,此时value就为config的信息);
					 *
					 * 3、导入的普通类,将当前candidate结合configClass(该信息主要用于candidate记录是被谁导入的)生成新的configClass,
					 * 递归调用processConfigurationClass
					 *
					 *
					 * 总结:
					 * A、针对导入的ImportSelect实现类以及普通类,最终都会执行processConfigurationClass,作为Configuration放入
					 * private final Map configurationClasses = new LinkedHashMap<>()
					 * 但是都还没注册到beanDefinitionMap中,通过后续的	this.reader.loadBeanDefinitions(configClasses)实现注册
					 * beanDefinitionMap。
					 *
					 * B、实现ImportBeanDefinitionRegistrar接口的类,未放入configurationClasses中,将实现类标记在ConfigurationClass中
					 * importBeanDefinitionRegistrars的属性上,最终通过this.reader.loadBeanDefinitions(configClasses)实现注册
					 *
					 */
					if (candidate.isAssignable(ImportSelector.class)) {
     
						// 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);
							//因为导入的类可能也会有@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);
						//将导入注册类实例化后放入map中,map中的key为实例bean value为当前sourceClass的注解
						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中通过importBy 标记是通过哪个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();
			}
		}
	}

        上述流程如下图所示:
spring源码系列5——spring启动流程3_第2张图片

  • ImportSelector
    实例化ImportSelector的实现类,然后转化成SourceClass,递归调用processImports;

  • ImportBeanDefinitionRegistrar
    实例化ImportBeanDefinitionRegistrar的实现类,然后添加到
    Map importBeanDefinitionRegistrars =new LinkedHashMap<>()
    中,key为实例化对象,value为导入类的AnnotationMetadata;

  • 普通类
    将导入的普通类转成ConfigurationClass对象,然后调用processConfigurationClass方法。
    ConfigurationClass通过
    private final Set importedBy = new LinkedHashSet<>(1)
    属性记录是被哪个类引入的;

    下面以demo进行说明:

@Configuration
@Import({
     ImportBeanDefinitionRegistrarImpl.class, ImportSelectorImpl.class, ImportBean.class})
@ImportResource(value="classpath:spring-config.xml")
public class Config {
     

	/**
	 * Import总结,总共三种情况:1、引入普通的类;2、引入实现ImportSelector的类;3、引入ImportBeanDefinitionRegistrar实现的类
	 */

	@Bean
	public static Student user(){
     
		return new Student();
	}

}

public class ImportBeanDefinitionRegistrarImpl implements ImportBeanDefinitionRegistrar {
     

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
     

	}
}

public class ImportSelectorImpl implements ImportSelector {
     
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
     
		return new String[]{
     "xu.jiang.hua.im.ImportSelectBean"};
	}
}

public class ImportBean {
     
}

解析完成后,如下所示:
spring源码系列5——spring启动流程3_第3张图片

以上是 ConfigurationClassParser类属性
private final Map configurationClasses = new LinkedHashMap<>()的值;

  • ImportSelectBean作为ImportSelectort接口实现的导入类,未被注册到BeanDefinitionMap中,此处importBy记录ImportSelectBean被Config导入,如红框1所示;

  • ImportBean作为@Import注解直接导入的类,未被注册到BeanDefinitionMap中,此处importBy记录ImportBean被Config导入,如红框3所示;

  • ImportBeanDefinitionRegistrarImpl未生成相应的对象放入configurationClasses 中,但是被记录在Config类中的importBeanDefinitionRegistrars的map中,如红框8所示;

  • Config类中的@Bean方法被记录在Config类所对应Configuration中的beanMethods,如6所示;

  • Config类中的@ImportSource注解导入的配置存储在Config类所对应Configuration中的ImporedResource;

2、this.reader.loadBeanDefinitions(configClasses)

	public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
     
		TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
		//遍历所有的configurationClass
		for (ConfigurationClass configClass : configurationModel) {
     
			loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
		}
	}
	private void loadBeanDefinitionsForConfigurationClass(
			ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
     

        //忽略非核心代码

		//如果configClass被Import引入,未注入到beanDefinitionMap中,此处册到beanDefinitionMap中
		if (configClass.isImported()) {
     
			registerBeanDefinitionForImportedConfigurationClass(configClass);
		}
		//如果configClass里面的@Bean注解,此处对@Bean标记的bean注册到beanDefinitionMap中
		for (BeanMethod beanMethod : configClass.getBeanMethods()) {
     
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}
        //当前类导入的ImportResources
		loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());

		//处理当前类导入的ImportBeanDefinitionRegistrars
		loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
	}
  • @Import注解导入以及ImportSelector
	private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
     
		AnnotationMetadata metadata = configClass.getMetadata();
		AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);

		ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
		configBeanDef.setScope(scopeMetadata.getScopeName());
		String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
		//主要处理:Primary Role DependOn  Description
		AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);

		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
		//注册到beanDefinitionMap中
		this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
		configClass.setBeanName(configBeanName);

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

ImportBean和ImportSelectBean所对应的importBy非空,会被上述方法注册到容器;

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

回调ImportBeanDefinitionRegistrars接口的实现。

  • @Bean

对于Config类里面有@Bean标记的方法,因此Config class所对应的Configuration里面的beanMethods为非空,如下所示:
spring源码系列5——spring启动流程3_第4张图片

则会调用以下方法:

	private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
     
		ConfigurationClass configClass = beanMethod.getConfigurationClass();
		MethodMetadata metadata = beanMethod.getMetadata();
		String methodName = metadata.getMethodName();

		// Do we need to mark the bean as skipped by its condition?
		if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
     
			configClass.skippedBeanMethods.add(methodName);
			return;
		}
		if (configClass.skippedBeanMethods.contains(methodName)) {
     
			return;
		}

		AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
		Assert.state(bean != null, "No @Bean annotation attributes");

		// Consider name and any aliases
		//优先从注解里面取属性name,如果为空 则直接取方法名为beanName
		List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
		String beanName = (!names.isEmpty() ? names.remove(0) : methodName);

		// Register aliases even when overridden 
		//注册别名
		for (String alias : names) {
     
			this.registry.registerAlias(beanName, alias);
		}

		// Has this effectively been overridden before (e.g. via XML)?
		if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
     
			if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {
     
				throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
						beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() +
						"' clashes with bean name for containing configuration class; please make those names unique!");
			}
			return;
		}

		ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
		beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));

		if (metadata.isStatic()) {
     
			// static @Bean method
			beanDef.setBeanClassName(configClass.getMetadata().getClassName());
			beanDef.setFactoryMethodName(methodName);
		}
		else {
     
			// instance @Bean method
			beanDef.setFactoryBeanName(configClass.getBeanName());
			beanDef.setUniqueFactoryMethodName(methodName);
		}
		beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
		beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.
				SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);

		AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);

		Autowire autowire = bean.getEnum("autowire");
		if (autowire.isAutowire()) {
     
			beanDef.setAutowireMode(autowire.value());
		}

		boolean autowireCandidate = bean.getBoolean("autowireCandidate");
		if (!autowireCandidate) {
     
			beanDef.setAutowireCandidate(false);
		}

		String initMethodName = bean.getString("initMethod");
		if (StringUtils.hasText(initMethodName)) {
     
			beanDef.setInitMethodName(initMethodName);
		}

		String destroyMethodName = bean.getString("destroyMethod");
		beanDef.setDestroyMethodName(destroyMethodName);

		// Consider scoping
		ScopedProxyMode proxyMode = ScopedProxyMode.NO;
		AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class);
		if (attributes != null) {
     
			beanDef.setScope(attributes.getString("value"));
			proxyMode = attributes.getEnum("proxyMode");
			if (proxyMode == ScopedProxyMode.DEFAULT) {
     
				proxyMode = ScopedProxyMode.NO;
			}
		}

		// Replace the original bean definition with the target one, if necessary
		BeanDefinition beanDefToRegister = beanDef;
		if (proxyMode != ScopedProxyMode.NO) {
     
			BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
					new BeanDefinitionHolder(beanDef, beanName), this.registry,
					proxyMode == ScopedProxyMode.TARGET_CLASS);
			beanDefToRegister = new ConfigurationClassBeanDefinition(
					(RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata);
		}

		if (logger.isTraceEnabled()) {
     
			logger.trace(String.format("Registering bean definition for @Bean method %s.%s()",
					configClass.getMetadata().getClassName(), beanName));
		}
		//注册到beanDefinitionMap
		this.registry.registerBeanDefinition(beanName, beanDefToRegister);
	}
  • @ImportSource
    通过xmlBeanDefinitionReader加载对应的配置文件实现bean的注册;

3、本节总结

本节第1步部分分析了@Import、@ImportResouce等注解导入的类如何在所对应类的Configuration中表示;第2部分分析如何将@Import、@Bean、@ImportResource标记的类注册到beanDefintionMap中;截止到目前,已经完成容器如何将spring管理的类注册到beanDefinitionMap的分析(除自动装配外,作为专题单独分析),下一节分析spring如何实例化bean。

你可能感兴趣的:(spring,spring,bean)