Spring实例化源码解析(二)

ConfigurationClassPostProcessor源码 解析

书接上回,在第一次调用invokeBeanDefinitionRegistryPostProcessors方法的时候参数currentRegistryProcessors为ConfigurationClassPostProcessor,本章主要深入这个类的postProcessBeanDefinitionRegistry方法。

postProcessBeanDefinitionRegistry

首先看到的这句注释就能完整的概括这章源码的全部内容:从注册表中的配置类派生更多的bean definitions。刚看这句话可能理解不出来里面的意思,那就各位看官里面请。

	/**
	 * Derive further bean definitions from the configuration classes in the registry.
	 */
	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		int registryId = System.identityHashCode(registry);
		if (this.registriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
		}
		if (this.factoriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanFactory already called on this post-processor against " + registry);
		}
		this.registriesPostProcessed.add(registryId);

		processConfigBeanDefinitions(registry);
	}

首先为BeanDefinitionRegistry生成了一个registryId,这个就是为了检验重复加载的问题,所以前面的都只是校验,如果只看主流程这些你都可以不看,直接关注最后一行代码,processConfigBeanDefinitions(registry)。

Spring实例化源码解析(二)_第1张图片

上面这张图可以知道registry中的具体属性内容,需要我们关注的是BeanDefinitionNames和beanPostProcessors这两个。aopConfig是我启动的时候的配置类,也就是我手动注册的类(register(componentClasses)),其他的都是spring自带的,本章的ConfigurationClassPostProcessor就是。

processConfigBeanDefinitions

开始进入正题,接下来就慢慢的拆解processConfigBeanDefinitions方法。

		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		// 当前BeanFacoty中存在的BeanDefinitonNames
		String[] candidateNames = registry.getBeanDefinitionNames();

		for (String beanName : candidateNames) {
			// 根据名称获取BeanDefinition
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
			// 检测配置候选人的条件,此处为true才会加入配置候选人
			// private static final Set candidateIndicators = new HashSet<>(8);
			//
			//	static {
			//		candidateIndicators.add(Component.class.getName());
			//		candidateIndicators.add(ComponentScan.class.getName());
			//		candidateIndicators.add(Import.class.getName());
			//		candidateIndicators.add(ImportResource.class.getName());
			//	}
			// 也就是说只要包含这些也都是配置候选人
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}

		// Return immediately if no @Configuration classes were found
		if (configCandidates.isEmpty()) {
			return;
		}

首先定义了一个configCandidates的集合,我们把他叫做后续按配置类集合,存放的是BeanDefinitionHolder,BeanDefinitionHolder和BeanDefinition是Spring框架中两个相关的概念,它们之间存在一种包含关系。

BeanDefinition是一个接口,用于描述一个bean的定义信息,包括bean的类名、作用域、依赖关系、属性值等信息。它是Spring框架中定义bean的元数据的核心表示形式。

BeanDefinitionHolder是一个包装类,用于持有BeanDefinition对象以及与之关联的bean名称。它包含两个主要成员变量:bean名称和BeanDefinition对象。

在Spring框架的内部,BeanDefinitionHolder常用于在注册表中存储和管理bean的定义。它提供了一个包装机制,使得可以将bean名称与其对应的BeanDefinition关联起来,并一起存储在注册表中。

通过BeanDefinitionHolder,可以轻松地访问和操作与特定bean相关联的BeanDefinition。它提供了一种方便的方式来获取bean的名称、获取和设置BeanDefinition的属性值等操作。

candidateNames是一个string数组,从上面的截图可以看出,当前会有5个BeanDefinitionNames,然后进入for循环,for循环要做的事情就是把这些BeanDefinition拿出来判断是否是候选配置类。

else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}

ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)方法就是用来判断是否是候选类,可以看出configCandidates.add方法就只在这里用到了。

public static boolean checkConfigurationClassCandidate(
			BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {

		String className = beanDef.getBeanClassName();
		if (className == null || beanDef.getFactoryMethodName() != null) {
			return false;
		}

		AnnotationMetadata metadata;
		if (beanDef instanceof AnnotatedBeanDefinition &&
				className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
			// Can reuse the pre-parsed metadata from the given BeanDefinition...
			metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
		}
		else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
			// Check already loaded Class if present...
			// since we possibly can't even load the class file for this Class.
			Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
			if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
					BeanPostProcessor.class.isAssignableFrom(beanClass) ||
					AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
					EventListenerFactory.class.isAssignableFrom(beanClass)) {
				return false;
			}
			metadata = AnnotationMetadata.introspect(beanClass);
		}
		else {
			try {
				MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
				metadata = metadataReader.getAnnotationMetadata();
			}
			catch (IOException ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("Could not find class file for introspecting configuration annotations: " +
							className, ex);
				}
				return false;
			}
		}

		Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
		if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
		}
		else if (config != null || isConfigurationCandidate(metadata)) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
		}
		else {
			return false;
		}

		// It's a full or lite configuration candidate... Let's determine the order value, if any.
		Integer order = getOrder(metadata);
		if (order != null) {
			beanDef.setAttribute(ORDER_ATTRIBUTE, order);
		}

		return true;
	}

虽然上述的代码看起来很长,而且杂七杂八的逻辑判断让我们不知道如何下手,这个时候可以打端点调试一下。

Spring实例化源码解析(二)_第2张图片

唉哟,beanDef是RootBeanDefinition类型,所以会进入else if的逻辑里面,也就到了我们这段代码的第一个核心逻辑。因为这里有个return,所以在看的时候需要关注一下。

Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
			if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
					BeanPostProcessor.class.isAssignableFrom(beanClass) ||
					AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
					EventListenerFactory.class.isAssignableFrom(beanClass)) {
				return false;
			}

这段代码用于过滤掉特定类型的bean,不对它们应用后续的处理。

具体来说,改代码中的条件判断用于检查给定的beanClass是否属于以下四个类型之一:

1、BeanFactoryPostProcessor:实现了BeanFactoryPostProcessor接口的类,用于在容器实例化任何其他bean之前对bean工厂进行自定义修改。

2、BeanPostProcessor:实现了BeanPostProcessor接口的类,用于在容器实例化bean时对bean进行自定义处理,例如初始化前后的操作。

3、AopInfrastructureBean:实现了AopInfrastructureBean接口的类,用于定义AOP基础设施bean,如AOP代理工厂等。

4、EventListenerFactory:实现了EventListenerFactory接口的类,用于创建事件监听的工厂。

如果给定的beanClass是上述四个类型之一,那么该代码会返回false,表示不对该类型的bean应用后续的处理。

打端点之后其实可以知道Spring自己注入的那些都会被跳过,唯独就是我们手动registry(AopConfig.class)

Spring实例化源码解析(二)_第3张图片

else {
			try {
				MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
				metadata = metadataReader.getAnnotationMetadata();
			}
			catch (IOException ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("Could not find class file for introspecting configuration annotations: " +
							className, ex);
				}
				return false;
			}
		}

		Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
		if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
		}
		else if (config != null || isConfigurationCandidate(metadata)) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
		}
		else {
			return false;
		}

		// It's a full or lite configuration candidate... Let's determine the order value, if any.
		Integer order = getOrder(metadata);
		if (order != null) {
			beanDef.setAttribute(ORDER_ATTRIBUTE, order);
		}

然后我们可以知道metadataReader就是元数据的读取,然后看是否含有@Configuration注解,我们的AopConfig其实是没有@Configutation注解的。

// 容器启动
AnnotationConfigApplicationContext annotationConfigApplicationContext =
       new AnnotationConfigApplicationContext(AopConfig.class);

// AopCofig类,类似于SpringBoot的Application启动类
@ComponentScan(value = {"com.qhyu.cloud.**"})
public class AopConfig {
	// 啥也没有
}

所以第二个核心逻辑就是isConfigurationCandidate(matedata),ConfigurationClassUtils类中。

public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
		// Do not consider an interface or an annotation...
		if (metadata.isInterface()) {
			return false;
		}

		// Any of the typical annotations found?
		for (String indicator : candidateIndicators) {
			if (metadata.isAnnotated(indicator)) {
				return true;
			}
		}

		// Finally, let's look for @Bean methods...
		return hasBeanMethods(metadata);
	}

如果是个接口直接返回false,说明得是类才可以。candidateIndicators是一个静态的全局变量,内容是static静态代码块里加载的。

private static final Set<String> candidateIndicators = new HashSet<>(8);

	static {
		candidateIndicators.add(Component.class.getName());
		candidateIndicators.add(ComponentScan.class.getName());
		candidateIndicators.add(Import.class.getName());
		candidateIndicators.add(ImportResource.class.getName());
	}

很明朗了对吧。就是包含这些注解的,都会被加入到configCandidates配置后端名单中。

跳出方法查看beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);这行代码将一个名为 CONFIGURATION_CLASS_ATTRIBUTE 的属性设置为值 CONFIGURATION_CLASS_LITE。

在Spring中,CONFIGURATION_CLASS_ATTRIBUTE是一个常量,用于指示配置类的类型。它通常用于标识配置类是以何种方式加载和处理的。

CONFIGURATION_CLASS_LITE 和 CONFIGURATION_CLASS_FULL 是两种可能的取值,表示不同类型的配置类:

1、CONFIGURATION_CLASS_LITE:表示轻量级配置类。这种类型的配置类通常是通过@Configuration注解进行标记的,但不包含任何特殊的逻辑处理。它们可能指示简单地定义一些bean的声明,而没有涉及复杂的依赖注入、条件化配置或其他高级功能。轻量级配置类在处理过程中会更加简单和高效。

2、CONFIGURATION_CLASS_FULL:表示完全配置类。这种类型的配置类包含了更多的复杂逻辑和功能,如条件配置、依赖注入、AOP、事件处理等。完全配置类可能使用了更多的Spring功能和特性,需要进行更全面的解析和处理。

重回processConfigBeanDefinitions

去过了最远的地方,还得回到最原来的地方。

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		//registry 也就是BeanFactory(ConfigurableListableBeanFactory)
		// configCandidates Candidates候选人
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		// 当前BeanFacoty中存在的BeanDefinitonNames
		String[] candidateNames = registry.getBeanDefinitionNames();

		for (String beanName : candidateNames) {
			// 根据名称获取BeanDefinition
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
			// 检测配置候选人的条件,此处为true才会加入配置候选人
			// private static final Set candidateIndicators = new HashSet<>(8);
			//
			//	static {
			//		candidateIndicators.add(Component.class.getName());
			//		candidateIndicators.add(ComponentScan.class.getName());
			//		candidateIndicators.add(Import.class.getName());
			//		candidateIndicators.add(ImportResource.class.getName());
			//	}
			// 也就是说只要包含这些也都是配置候选人
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}

		// Return immediately if no @Configuration classes were found
		if (configCandidates.isEmpty()) {
			return;
		}

当ConfigurationClassUtils类的isConfigurationCandidate返回true之后就说明ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)返回true。所以会把当前beanDefinitionHolder加入到配置候选集合中。当然如果配置候选集合为空就直接return了。

接下来这个就是单纯的排序,这边直接跳过,不耽误时间,有兴趣自己深入,或者后续单独讲一期排序。

// Sort by previously determined @Order value, if applicable
		configCandidates.sort((bd1, bd2) -> {
			int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
			int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
			return Integer.compare(i1, i2);
		});

SingletonBeanRegistry sbr = null;
		if (registry instanceof SingletonBeanRegistry) {
			sbr = (SingletonBeanRegistry) registry;
			if (!this.localBeanNameGeneratorSet) {
				BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
						AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
				if (generator != null) {
					this.componentScanBeanNameGenerator = generator;
					this.importBeanNameGenerator = generator;
				}
			}
		}

		if (this.environment == null) {
			this.environment = new StandardEnvironment();
		}

排序下面这段代码用于获取并设置组件扫描时使用的Bean名称生成器。

首先定义了一个SingletonBeanRegistry类型的变量sbr并初始化为null。然后,通过检查registry对象是否是SingletonBeanRegistry的实例来确定是否可以进行后续的操作。

如果registry是SingletonBeanRegistry的实例,说明它是一个单例Bean注册表,可以用于获取和设置单例Bean。接下来,讲registry对象强制转换为SingletonBeanRegistry类型,并将其赋值给sbr变量。

代码尝试从sbr中获取名为AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR的单例Bean。这个常量是用于指定配置类的Bean名称生成器的键。如果能够获取到该单例Bean,说明已经为配置类设置了特定的Bean名称生成器。如果generator不为null,代码将获取到的generator赋值给两个成员变量:componentScanBeanNameGenerator和importBeanNameGenerator。这两个成员变量分别用于组件扫描和导入Bean时使用的Bean名称生成器。

通过这段代码,可以实现以下功能:

  • 检查是否有合适的单例Bean注册表可用。
  • 获取配置类的Bean名称生成器。
  • 设置组件扫描和导入Bean时使用的Bean名称生成器。

这样做的目的是为了在Spring的组件扫描和Bean加载过程中使用适当的Bean名称生成器,以确保生成的Bean名称符合预期并与其他组件协调一致。

// Parse each @Configuration class
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);

		// 这是候选人Set,去重
		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
		// 以及解析了的配置类
		Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
		do {
			StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
			// 解析我们的配置类,我们使用AnnotationConfigApplicationContent启动的时候,使用的构造函数进行创建
			// 也就是加入了@ComponentScan注解,需要根据里面的路径扫描包路径,从而找到所有需要被Spring管理的Bean的beanDefiniton信息
			// ConfigurationClassParser去解析我们的AopConfig类.这个类只会在启动的时候呗调用一次
			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());
			}
			// ImportBeanDefinitionRegistrar扫描实现了这个接口的方法
			// ConfigurationClassParser解析的在这里又读出来,这里需要打断点再观察一下。
			this.reader.loadBeanDefinitions(configClasses);
			alreadyParsed.addAll(configClasses);
			processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();

			candidates.clear();
			if (registry.getBeanDefinitionCount() > candidateNames.length) {
				String[] newCandidateNames = registry.getBeanDefinitionNames();
				Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
				Set<String> alreadyParsedClasses = new HashSet<>();
				for (ConfigurationClass configurationClass : alreadyParsed) {
					alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
				}
				for (String candidateName : newCandidateNames) {
					if (!oldCandidateNames.contains(candidateName)) {
						BeanDefinition bd = registry.getBeanDefinition(candidateName);
						if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
								!alreadyParsedClasses.contains(bd.getBeanClassName())) {
							candidates.add(new BeanDefinitionHolder(bd, candidateName));
						}
					}
				}
				candidateNames = newCandidateNames;
			}
		}
		while (!candidates.isEmpty());

这段代码是最最最核心的内容,还会引入一个新的类来解析和处理候选配置类,本章我们主要梳理这段代码的逻辑,深入的源码将在下一章节来分析。

1、创建一个ConfigurationClassParser对象并进行初始化。该对象用于解析候选配置类,并生成相应的配置类对象(ConfigurationClass)和相关的Bean定义。

2、创建一个Set类型的变量candidates,用于存储候选的Bean定义持有者。这些候选人是从configCandidates中获取的,它是一个包含待解析的候选配置类的集合。

3、创建一个Set类型的变量alreadyParsed,用于存储已经解析过的配置类。

4、进入循环,直到候选人集合为空。在每次循环迭代中,执行以下操作:
a. 启动一个性能追踪步骤,记录解析过程的性能指标。
b. 调用parser.parse(candidates)方法,解析candidates中的候选配置类。在解析过程中,将根据配置类的内容创建相应的Bean定义。
c. 调用parser.validate()方法,对解析后的配置类进行验证,确保它们符合规范。
d. 获取解析后的配置类集合,并将其与已解析的配置类集合进行比较,筛选出新增的配置类。
e. 如果this.reader为null,则创建一个ConfigurationClassBeanDefinitionReader对象,用于读取模型并基于其内容创建Bean定义。
f. 调用this.reader.loadBeanDefinitions(configClasses)方法,将配置类转换为Bean定义,并注册到Bean定义注册表中。
g. 将已解析的配置类添加到alreadyParsed集合中。
h. 根据新的Bean定义数量更新candidateNames数组,并筛选出新增的候选人Bean定义。
i. 清空candidates集合,准备下一次循环迭代。

整个过程会迭代解析和处理所有的候选配置类,将它们转换为相应的Bean定义,并注册到Bean定义注册表中。这样,这些候选配置类中声明的Bean就可以在应用程序中使用和管理了。

总结

本章就ConfigurationClassPostProcessor的processConfigBeanDefinitions方法做了深入的源码分析,描述了方法内部所完成的spring实例化的过程,具体的在项目启动过程中如何将我们自己定义的需要被spring管理的bean的定义信息放入工厂中,会在下一个章节进行详细的分析。

你可能感兴趣的:(Spring专题,spring,java,后端)