ConfigurationClassPostProcessor

目录

文档翻译

SourceClass 

ConfigurationClass

作为BeanDefinitionRegistryPostProcessor的扩展回调

 processConfigBeanDefinitions(BeanDefinitionRegistry registry)片段一 :过滤出是配置类的beanName

 processConfigBeanDefinitions(BeanDefinitionRegistry registry)片段二:使用ConfigurationClassParser解析候选的配置类

ConfigurationClassParser文档翻译

ConfigurationClassParser#doProcessConfigurationClass:文档翻译

ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)  片段一:优先处理内配置类

ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)  片段二:解析@ComponentScan并处理扫描出的潜在的配置类(包含Full模式和Lite模式的配置类)

 ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)  片段三:处理@Import

 ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)  片段四:处理@Bean方法

 ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)  片段五:处理接口上的@Bean

 processConfigBeanDefinitions(BeanDefinitionRegistry registry)片段三:注册之前收集到ConfigurationClass中的@Bean,ImportBeanDefinitionRegistrar类型,@Import相关的BeanDefinition

processConfigBeanDefinitions(BeanDefinitionRegistry registry)片段四:解析可能被遗漏的潜在的配置类

作为BeanFactoryPostProcessor的扩展回调


文档翻译

用于对@Configuration类进行引导处理。此后处理器按优先级排序,因为在任何其他BeanFactoryPostProcessor执行之前, @Configuration类中声明的任何Bean方法都必须注册其对应的Bean定义,这一点很重要。

SourceClass 

简单的包装器,允许以统一的方式处理带注释的源类,而不管它们是如何加载的。

ConfigurationClass

表示用户定义的@Configuration类。
以“扁平化”方式包含一组Bean方法,包括在类的祖先中定义的所有此类方法

作为BeanDefinitionRegistryPostProcessor的扩展回调

    @Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        ...
		processConfigBeanDefinitions(registry);
	}

 processConfigBeanDefinitions(BeanDefinitionRegistry registry) 处理配置类型的bean定义。分段解析如下:

 processConfigBeanDefinitions(BeanDefinitionRegistry registry)片段一 :过滤出是配置类的beanName

		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		String[] candidateNames = registry.getBeanDefinitionNames();

		for (String beanName : candidateNames) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
					ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}

checkConfigurationClassCandidate:检查给定的bean定义是否是配置类。分为两种情况:

  1. 类被@Configuration修饰 (Full模式)
  2. 类被@Component,@ComponentScan,@Import,@ImportResource修饰,或类上不含有这些注解,但是方法被@Bean修饰。(Lite(精简)模式)。

到目前为止就只有启动类在配置后候选者集合中,有其它的也不用管,处理方式都是一样的

 processConfigBeanDefinitions(BeanDefinitionRegistry registry)片段二:使用ConfigurationClassParser解析候选的配置类

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

		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
		Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
		do {
			parser.parse(candidates);

ConfigurationClassParser文档翻译

解析Configuration类定义,填充ConfigurationClass类对象的集合(解析单个配置类可能会产生任意数量的配置类对象,因为一个配置类可以使用Import注释导入另一个配置类)。此类有助于将【解析 Configuration 类的结构的关注点】与【基于模型内容注册 BeanDefinition 对象的关注点】分开。

ConfigurationClassParser#parse(Set<BeanDefinitionHolder> configCandidates)。因为启动类BeanDefinition属于AnnotatedBeanDefinition,so继续parse。

	public void parse(Set<BeanDefinitionHolder> configCandidates) {
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
				if (bd instanceof AnnotatedBeanDefinition) {
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				}
				else if 
				else 
			}
		}
		this.deferredImportSelectorHandler.process();
	}
	protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
		processConfigurationClass(new ConfigurationClass(metadata, beanName));
	}

processConfigurationClass(ConfigurationClass configClass):处理配置类 

	protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
        //1
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			return;
		}
        //2
		ConfigurationClass existingClass = this.configurationClasses.get(configClass);
		if (existingClass != null) {
			if (configClass.isImported()) {
				if (existingClass.isImported()) {
					existingClass.mergeImportedBy(configClass);
				}
				return;
			}
			else {
                //找到明确的bean定义,可能替换了导入。让我们删除旧的,然后使用新的。
				this.configurationClasses.remove(configClass);
				this.knownSuperclasses.values().removeIf(configClass::equals);
			}
		}
        //3
		SourceClass sourceClass = asSourceClass(configClass);
		do {
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
		while (sourceClass != null);
        //4
		this.configurationClasses.put(configClass, configClass);
	}
  • //1 根据@Conditional批注确定是否应跳过。
  • //2 是否缓存过该configClass的key,有的话获取到之前的配置类。.如果该configClass是被其它配置类导入的,则合并该两者被其它配置类导入的配置类。
  • //3 递归处理配置类及其超类层次结构。对于启动类来说,SourceClass就只是简单的包装了启动类的class。

先讲下//2出被导入的含义。比如下面代码。B和C都被A导入。即内部配置类C和@Import修饰的类B都算作被主类A导入。B和C作为ConfigurationClass被解析时,内部都会维护Set<ConfigurationClass> importedBy这样一个成员变量,即我是被谁导入的,值都为A对应的ConfigurationClass。

@Import(B.class)

@Configuration

public class A{

  @Configuration

  public static class C{}

}

从//4处代码可以看出configurationClasses(map结构)键和值是一样的,那么/如果existingClass 不为空,则existingClass 和configClass对应的内存地址是一样的。那为啥会有下面这样的代码呢?这不是多次一举吗?

            if (configClass.isImported()) {
                if (existingClass.isImported()) {

其实内存地址是不一样的。看一看configClass的equals方法就知道,这个configClass不一定存在于该map中。下面举例说明什么情况下会发生这种情况。DispatcherServletAutoConfiguration还有其它内部类,这里只以这两个说明。(看完后面的再看这块,不然。。。当然熟悉下面流程的可以直接看)。

 DispatcherServletAutoConfiguration是spring内部的一个自动配置类。要知道spring是先会判断是否有内部配置类的,有的话先解析。还有就是也会先解析@Import对应的配置类。获取内部类调用class如下方法,数组顺序和定义相反。开始分析:

Class<?>[] declaredClasses = sourceClass.getDeclaredClasses();

  1. parse DispatcherServletAutoConfiguration 发现有内部类,于是转头先解析内部类
  2. 先解析DispatcherServletRegistrationConfiguration,发现@Import(DispatcherServletConfiguration.class),于是转头先解析DispatcherServletConfiguration
  3. DispatcherServletConfiguration解析完丢入configurationClasses(map结构)中。DispatcherServletConfiguration对应的ConfigurationClass中的importedBy中存入了DispatcherServletRegistrationConfiguration
  4. 接着回头解析DispatcherServletRegistrationConfiguration,解析完丢入configurationClasses(map结构)中
  5. 要记得第二步是有两个内部类的,所以这里会再次解析DispatcherServletConfiguration,这里解析时会重新新建一个ConfigurationClass ,这个ConfigurationClass的importedBy中存入了 DispatcherServletAutoConfiguration。所以这时从configurationClasses(map结构)根据这个新建的ConfigurationClass是可以获取到之前的值的,也就是existingClass。也就是第3步中的ConfigurationClass(importedBy中存入了DispatcherServletRegistrationConfiguration),所以上面连个if都为true,然后合并两者的importedBy中的值。即最终DispatcherServletConfiguration对应的ConfigurationClass中的importedBy有两个值。


ConfigurationClassParser#doProcessConfigurationClass:文档翻译

通过从源类(sourceClass)中读取注解、成员和方法来应用处理并构建一个完整的ConfigurationClass 。当相关来源被发现时,可以多次调用此方法。

ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)  片段一:优先处理内配置类

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {

		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// Recursively process any member (nested) classes first
			processMemberClasses(configClass, sourceClass);
		}
		// Process any @PropertySource annotations
        //处理任何@PropertySource批注
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
            ......
		}

processMemberClasses(configClass, sourceClass):首先递归处理任何成员(嵌套)类。简单理解:查找该sourceClass的内部类(除去父类的),判断该内部类是否为配置类的候选者,如果是则优先处理这些配置类。处理时调用上述ConfigurationClassParser#processConfigurationClass(ConfigurationClass configClass)处理配置类方法。

@PropertySource跳过。

小结:相当于是先处理完当前配置类所有内部类是配置类的,在处理当前配置类。 

ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)  片段二:解析@ComponentScan并处理扫描出的潜在的配置类(包含Full模式和Lite模式的配置类)

		// Process any @ComponentScan annotations
        //处理任何@ComponentScan 批注,AnnotationAttributes包含注解的所有信息
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
                //1
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}
  • componentScanParser:ComponentScanAnnotationParser,@ComponentScan批注的解析器。
  • ComponentScanAnnotationParser#parse:创建ClassPathBeanDefinitionScanner,将@ComponentScan对应的值初始化到这个扫描器上。同时会获取要扫描的basePackages。当@ComponentScan属性basePackages和basePackageClasses对应的包名如果都为空则使用declaringClass对应的包名作为添加到basePackages,对于启动类来说就是启动类所在的包名。 
  • //1 检查扫描出的bean定义集是否有配置类(包含Full模式和Lite模式的配置类),并在需要时递归解析(若是配置类候选者,则parse这些配置类,这就是该方法会多次调用的其中一个原因)。这里递归就可以解析自定义的配置类。递归解析:配置类中又有@ComponentScan或者后面说的@Import,无限循环。

 ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)  片段三:处理@Import

		// Process any @Import annotations
        //处理任何@Import批注
		processImports(configClass, sourceClass, getImports(sourceClass), true);

		// Process any @ImportResource annotations
        //处理任何@ImportResource 批注
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

我的这篇文章对于@import的详解

@ImportResource 跳过

 ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)  片段四:处理@Bean方法

		// Process individual @Bean methods
        //处理@Bean方法
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}
  • retrieveBeanMethodMetadata:检索所有@Bean方法的元数据。
  • 然后将这些beanMethod对应的MethodMetadata包装成BeanMethod。添加到该configClass中

 ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)  片段五:处理接口上的@Bean


		// Process default methods on interfaces
        //1
		processInterfaces(configClass, sourceClass);

		// Process superclass, if any
        //处理超类
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
                //找到超类,返回其注释元数据并递归
				return sourceClass.getSuperClass();
			}
		}

		// No superclass -> processing is complete
        //没有超类->处理完成
		return null;
  •   //1:递归查找sourceClass所有接口上被@Bean修饰的方法添加到configClass的BeanMethod中。类似下面这样的,也是可以的。

public interface InterfaceBean {
    @Bean
    default BeanDd BeanDd(){return new BeanDd();}
}

 processConfigBeanDefinitions(BeanDefinitionRegistry registry)片段三:注册之前收集到ConfigurationClass中的@Bean,ImportBeanDefinitionRegistrar类型,@Import相关的BeanDefinition

            //验证每个ConfigurationClass对象。
            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());
			}
			this.reader.loadBeanDefinitions(configClasses);
  • ConfigurationClassBeanDefinitionReader:读取给定的完全填充的ConfigurationClass实例集,并根据其内容在给定的BeanDefinitionRegistry注册Bean定义。
  • loadBeanDefinitions(Set<ConfigurationClass> ):循环读ConfigurationClass ,为类本身及其所有Bean方法注册Bean定义。同时该方法会处理该configClass收集到的ImportBeanDefinitionRegistrar,调用其registerBeanDefinitions方法注册beanDefinition。

processConfigBeanDefinitions(BeanDefinitionRegistry registry)片段四:解析可能被遗漏的潜在的配置类

			alreadyParsed.addAll(configClasses);

			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());

简单解析:alreadyParsed存储已经处理过的configClass.。清空当前candidates数据,因为这是一个do while循环。判断的就是candidates是否为空。然后从所有的BeanDefinition中获取到没有被解析过configClass的且满足配置类继续do while循环。防止遗漏。例如某个配置类没在扫描的包下,但是呢该类在某一个类中被主动注册了,那么这里就可以解析该配置类

作为BeanFactoryPostProcessor的扩展回调

@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		int factoryId = System.identityHashCode(beanFactory);
		if (this.factoriesPostProcessed.contains(factoryId)) {
			throw new IllegalStateException(
					"postProcessBeanFactory already called on this post-processor against " + beanFactory);
		}
		this.factoriesPostProcessed.add(factoryId);
		if (!this.registriesPostProcessed.contains(factoryId)) {
			// BeanDefinitionRegistryPostProcessor hook apparently not supported...
			// Simply call processConfigurationClasses lazily at this point then.
			processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
		}

		enhanceConfigurationClasses(beanFactory);
		beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
	}

enhanceConfigurationClasses:对被@Configuration修饰的BeanDefinition进行增强处理。增强处理类为ConfigurationClassEnhancer:通过生成一个CGLIB子类来增强Configuration类,该子类与Spring容器进行交互,以遵守@Bean方法的bean作用域语义。 每个此类@Bean方法都将在生成的子类中被覆盖,仅当容器实际请求构造新实例时才委托给实际的@Bean方法实现。 否则,对此类@Bean方法的调用将作为对容器的引用, @Bean名称获取相应的bean。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值