ConfigurationClassPostProcessor

 

目次

 

ドキュメントの翻訳

BeanDefinitionRegistryPostProcessorの拡張コールバックとして

 processConfigBeanDefinitions(BeanDefinitionRegistryレジストリ)フラグメント1

 processConfigBeanDefinitions(BeanDefinitionRegistryレジストリ)フラグメント2

ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass、SourceClass sourceClass)片段一

ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass、SourceClass sourceClass)片段二

 ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass、SourceClass sourceClass)片段三

 ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass、SourceClass sourceClass)片段四

 ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass、SourceClass sourceClass)片段五

 processConfigBeanDefinitions(BeanDefinitionRegistryレジストリ)フラグメント3

BeanFactoryPostProcessorの拡張コールバックとして


ドキュメントの翻訳

@Configurationクラスをガイドするために使用されます。その後、プロセッサは優先度でソートされます。これは、@ Configurationクラスで宣言されたBeanメソッドは、他のBeanFactoryPostProcessorが実行される前に、対応するBean定義を登録する必要があるためです。

BeanDefinitionRegistryPostProcessorの拡張コールバックとして

    @Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		
		。。。。。。

		processConfigBeanDefinitions(registry);
	}

 processConfigBeanDefinitions(BeanDefinitionRegistryレジストリ)は、Configurationクラスのレジストリに基づいて構成モデルを構築および検証します。セグメンテーション分析は次のとおりです。

 processConfigBeanDefinitions(BeanDefinitionRegistryレジストリ)フラグメント1

		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定义是否是配置类(或在配置/组件类中声明的嵌套组件类,也要自动注册)的候选者,并进行相应标记。简单说就是判断有没有被@Configuration,@Component,@ComponentScan,@Import,@ImportResource,@Bean修饰。将被修饰的BeanDefinition添加到configCandidates(配置后候选者集合中)。到目前为止就只有启动类在配置后候选者集合中

        //checkConfigurationClassCandidate代码片段	
        //被configuration修饰标记
        if (isFullConfigurationCandidate(metadata)) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
		}
        //被其它注解修饰标记
		else if (isLiteConfigurationCandidate(metadata)) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
		}

 processConfigBeanDefinitions(BeanDefinitionRegistryレジストリ)フラグメント2

		// 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(ユーザー定義の@Configurationクラスを表します。クラスの祖先で定義されたすべてのメソッドを含む、「フラット」な方法でBeanメソッドのセットを含みます)オブジェクトコレクションを入力します。このクラスは、Configurationクラスの構造を解析する焦点と、モデルのコンテンツに基づいてBeanDefinitionオブジェクトを登録する焦点を分離するのに役立ちます。

ConfigurationClassParser#parse(Set <BeanDefinitionHolder> configCandidates)。

				if (bd instanceof AnnotatedBeanDefinition) {
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				}

スタートアップクラスBeanDefinitionはAnnotatedBeanDefinitionに属しているため、解析を続行します。

ConfigurationClassParser#parse(AnnotationMetadataメタデータ、String beanName) 

	protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
		processConfigurationClass(new ConfigurationClass(metadata, beanName));
	}

ConfigurationClassParser#processConfigurationClass(ConfigurationClass configClass):プロセス構成クラス 

	protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
        //根据@Conditional批注确定是否应跳过某个项目。
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			return;
		}
        //是否缓存过该configClass的key,有的话获取到value.如果该configClass有导入的其它配置类,则合并该configClass导入的其它配置和value对应的configClass导入的其它配置
		ConfigurationClass existingClass = this.configurationClasses.get(configClass);
		if (existingClass != null) {
			if (configClass.isImported()) {
				if (existingClass.isImported()) {
					existingClass.mergeImportedBy(configClass);
				}
				// Otherwise ignore new imported config class; existing non-imported class overrides it.
				return;
			}
			else {
				// Explicit bean definition found, probably replacing an import.
				// Let's remove the old one and go with the new one.
                //找到明确的bean定义,可能替换了导入。让我们删除旧的,然后使用新的。
				this.configurationClasses.remove(configClass);
				this.knownSuperclasses.values().removeIf(configClass::equals);
			}
		}

		// Recursively process the configuration class and its superclass hierarchy.
        //从configClass找到递归找到SourceClass(一个简单的包装程序,使带注释的源类可以以统一的方式处理,而不管它们如何加载)。
		SourceClass sourceClass = asSourceClass(configClass);
		do {
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
        //上一步会返回超类的sourceClass,没有为null
		while (sourceClass != null);

		this.configurationClasses.put(configClass, configClass);
	}

スタートアップクラスの場合、SourceClassは単にスタートアップクラスをラップします。
ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass、SourceClass 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(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

processMemberClasses(configClass、sourceClass):最初に任意のメンバー(ネストされた)クラスを再帰的に処理します。簡単な理解:sourceClassの内部クラス(親クラスを除く)を見つけ、内部クラスが構成クラスの候補であるかどうかを判別し、候補である場合は、構成クラスが最初に処理されます。処理するときは、上記のConfigurationClassParser#processConfigurationClass(ConfigurationClass configClass)を呼び出して、構成クラスメソッドを処理します。概要:これは、現在の構成クラスを最初に処理することと同じです。すべての内部クラスは構成クラスであり、現在の構成クラスが処理されています。 

ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass、SourceClass sourceClass)片段二

		// 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
                //检查扫描的定义集是否有其他配置类,并在需要时递归解析(若是配置类候选者,则parse这些配置类,这就是该方法会多次调用的其中一个原因)
				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(AnnotationAttributes componentScan、final String declaringClass):ClassPathBeanDefinitionScannerを作成し、@ ComponentScanに対応する値をこのスキャナーに初期化します。同時に、スキャンされるbasePackagesが取得されます。@ComponentScan属性basePackagesおよびbasePackageClassesに対応するパッケージ名が空の場合、declaringClassに対応するパッケージ名がbasePackagesに追加されるパッケージ名として使用されます。 

 ClassPathBeanDefinitionScanner#doScan(String ... basePackages):指定された基本パッケージでスキャンを実行し、登録されたBean定義を返します。このメソッドは、注釈構成プロセッサーを登録しませんが、これを呼び出し元に知らせます。

	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
            //扫描类路径以查找候选组件。
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            //对beanDefinition进行设置,注册
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

findCandidateComponents(basePackage):クラスパスをスキャンして候補コンポーネントを見つけます。親クラスの特定のスキャン方法は次のとおりです。

 ClassPathScanningCandidateComponentProvider#scanCandidateComponents(String basePackage)

	private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
			for (Resource resource : resources) {
				if (traceEnabled) {
					logger.trace("Scanning " + resource);
				}
				if (resource.isReadable()) {
					try {
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
						if (isCandidateComponent(metadataReader)) {
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							sbd.setResource(resource);
							sbd.setSource(resource);
							if (isCandidateComponent(sbd)) {
								if (debugEnabled) {
									logger.debug("Identified candidate component class: " + resource);
								}
								candidates.add(sbd);
							}
							else {
								if (debugEnabled) {
									logger.debug("Ignored because not a concrete top-level class: " + resource);
								}
							}
						}
  1. basePackageの下にあるすべてのリソースを取得します。AntスタイルのPathMatcherを使用して、特定のロケーションパターンに一致するすべてのリソースを検索します。ファイルシステムでjarファイルとzipファイルおよびリソースをサポートします。
  2. すべてのリソースをトラバースし、リソースをSimpleMetadataReaderにラップします。SimpleMetadataReaderには、リソースアノテーションメタデータを読み取るAnnotationMetadataReadingVisitorが含まれます。ASMクラスビジター。クラス名と実装タイプ、およびクラスで定義されたアノテーションを検索し、それをAnnotationMetadataインターフェイスパブリックに渡します。 。
  3. metametaReaderが条件を満たしているかどうかを判別します。isCandidateComponent(MetadataReadermetadataReader)指定されたクラスがどの除外フィルターにも一致しないかどうか、および少なくとも1つの包含フィルターに一致するかどうかを判別します。
  4. 手順3が満たされると、ScannedGenericBeanDefinitionタイプのBeanDefinitionが生成され、BeanDefinitionが条件を満たしているかどうかが判別されます。isCandidateComponent(AnnotatedBeanDefinition beanDefinition)指定されたBean定義が候補条件を満たしているかどうかを判別します。デフォルトの実装では、クラスがインターフェイスではなく、囲まれたクラスに依存していないかどうかがチェックされます。このメソッドは、サブクラスでオーバーライドできます。満足している場合は、候補セットに追加します

 ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass、SourceClass sourceClass)片段三

		// 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);
			}
		}

getImports(SourceClass sourceClass):すべてのメタアノテーションを考慮し、@ Importクラスを返します。sourceClassアノテーションのアノテーションに@Importがあるかどうかを再帰的に見つけることです。ある場合は、対応する値をStringに変換し、それをSourceClassにラップして返します。
processImports(ConfigurationClass configClass、SourceClass currentSourceClass、Collection <SourceClass> importCandidates、boolean checkForCircularImports):前のステップで返されたデータを処理します。候補クラスはImportSelector->インポートを決定するためのデリゲートであり、候補クラスはImportBeanDefinitionRegistrar->他のBean定義を登録するためのデリゲートであり、候補クラスはImportSelectorまたはImportBeanDefinitionRegistrarではありません-> @ Configurationとして扱いますクラス。

例:mybatisによる@MapperScan。@Importがあり、値はMapperScannerRegistrarであり、ImportBeanDefinitionRegistrarインターフェースが実装されており、ここで処理されます。これは、MapperScannerRegistrarをインスタンス化して、Map <ImportBeanDefinitionRegistrar、AnnotationMetadata> importBeanDefinitionRegistrarsに格納するためのものです。対応するConfigurationClass。ConfigurationClassのimportBeanDefinitionRegistrarsマップは後で処理されます。

@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar

 ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass、SourceClass sourceClass)片段四

		// Process individual @Bean methods
        //处理@Bean方法
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}
  • retrieveBeanMethodMetadata:すべての@Beanメソッドのメタデータを取得します。beanMethodsには4つの値があり、これら4つの値のそれぞれにmethodMetadataSet値があることがわかります。 

 

  • 次に、これらのbeanMethodsに対応するMethodMetadataをBeanMethod(@Beanアノテーションでマークされた@Configurationクラスメソッドを表す)にラップし、それをconfigClassに追加します。

 ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass、SourceClass sourceClass)片段五


		// Process default methods on interfaces
        //递归查找sourceClass所有接口上被@Bean修饰的方法添加到configClass的BeanMethod中
		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;

 processConfigBeanDefinitions(BeanDefinitionRegistryレジストリ)フラグメント3

            //验证每个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);
			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());
ConfigurationClassBeanDefinitionReader:读取给定的完全填充的ConfigurationClass实例集,并根据其内容在给定的BeanDefinitionRegistry注册Bean定义。此类是根据BeanDefinitionReader层次结构建模的,但是由于一组配置类不是Resource ,因此未实现/扩展其任何构件。
ConfigurationClassBeanDefinitionReader#loadBeanDefinitions(Set<ConfigurationClass> configurationModel):读取configurationModel ,根据其内容在注册表中注册Bean定义。该方法循环调用下面方法。
ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator):读特定的ConfigurationClass ,为类本身及其所有Bean方法注册Bean定义。同时该方法会处理该configClass收集到的ImportBeanDefinitionRegistrar,调用其registerBeanDefinitions方法注册beanDefinition。mybatis的MapperScannerRegistrar就会在这里处理。

残りのコードの簡単な分析:alreadyParsedは、処理されたconfigClassを格納します。これはdowhileループであるため、現在の候補データをクリアします。判断は候補者が空であるかどうかです。次に、configClassが解析されていないすべてのBeanDefinitionsから取得し、構成クラスを満たして、dowhileループを続行します。

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です。Configurationクラスは、@ BeanメソッドのBeanスコープセマンティクスに準拠するためにSpringコンテナーと対話するCGLIBサブクラスを生成することによって拡張されます。このような各@Beanメソッドは、生成されたサブクラスでオーバーライドされ、コンテナーが実際に新しいインスタンスの構築を要求した場合にのみ、実際の@Beanメソッドの実装に委任されます。それ以外の場合、そのような@Beanメソッドの呼び出しはコンテナーへの参照として使用され、@ Bean名は対応するBeanを取得します。

 

おすすめ

転載: blog.csdn.net/sinat_33472737/article/details/113361028