Introduction to the working principle of ImportBeanDefinitionRegistrar

introduce

Simply put, during the startup process of the spring application, some classes annotated by @ Import (these classes implement the ImportBeanDefinitionRegistrar interface) will execute the registerBeanDefinitions method of ImportBeanDefinitionRegistrar , then generate a BeanDefinition object, and finally register it in the BeanDefinitionRegistry for subsequent instantiation The bean is prepared.

Where is it used?

Taking the spring boot application we are very familiar with as an example, the startup class contains
the @SpringBootApplication annotation

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    
    

Where @Import(AutoConfigurationImportSelector.class) is.

In addition, the annotation **@MapperScan** of spring integrated mybatis is also used.

Use process analysis

进入org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);

Go into the getImports method

	private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
			throws IOException {
    
    

		if (visited.add(sourceClass)) {
    
    
			for (SourceClass annotation : sourceClass.getAnnotations()) {
    
    
				String annName = annotation.getMetadata().getClassName();
				if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
    
    
					collectImports(annotation, imports, visited);
				}
			}
			imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
		}
	}

The SourceClass here is the current configuration class, what is a configuration class? Configuration classes are classes that contain annotations such as @Configuration, @Service, @Component, etc., such as our spring boot startup class.

The above code is equivalent to checking whether each configuration class contains the @Import annotation.

Come out and follow the above processImports method

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
    
    

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

		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
    
    
			this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
		}
		else {
    
    
			this.importStack.push(configClass);
			try {
    
    
				for (SourceClass candidate : importCandidates) {
    
    
					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);
							processImports(configClass, currentSourceClass, importSourceClasses, false);
						}
					}
					else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
    
    
						Class<?> candidateClass = candidate.loadClass();
						ImportBeanDefinitionRegistrar registrar =
								BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
						ParserStrategyUtils.invokeAwareMethods(
								registrar, this.environment, this.resourceLoader, this.registry);
						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());
						processConfigurationClass(candidate.asConfigClass(configClass));
					}
				}
			}
			catch (BeanDefinitionStoreException ex) {
    
    
				throw ex;
			}
			catch (Throwable ex) {
    
    
				throw new BeanDefinitionStoreException(
						configClass.getMetadata().getClassName() + "]", ex);
			}
			finally {
    
    
				this.importStack.pop();
			}
		}
	}

It is also very simple here, it is a few if else branch judgments,
among them,
if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class))
sees the ImportBeanDefinitionRegistrar interface, here is to add the Import class found above to
org.springframework.context.annotation .ConfigurationClass#importBeanDefinitionRegistrars in this Map.

By the way, the other
if (candidate.isAssignable(ImportSelector.class)) sentence is what we are familiar with for parsing spring boot automatic assembly, but it is of the ImportSelector type, which is not placed in the Map, and will be delayed later deal with it.

The above search process is to go through each configuration class. If there is ImportBeanDefinitionRegistrar, the Maps added to each of them are cached first.

Now that it's saved, it must be useful, right? Let's see.

Let me first explain that the ConfigurationClassPostProcessor class is very important. It parses all configuration classes and converts configuration classes into BeanDefinition objects. Subsequent creation of beans depends on it. The above call link is also initiated by it.

org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
其中有一句:
this.reader.loadBeanDefinitions(configClasses);

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    
    
		TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
		for (ConfigurationClass configClass : configurationModel) {
    
    
			loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
		}
	}

Traverse each ConfigurationClass configuration class, because we have parsed ImportBeanDefinitionRegistrar before, so these configuration classes contain the ImportBeanDefinitionRegistrar class.

private void loadBeanDefinitionsForConfigurationClass(
			ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
    
    

		if (trackedConditionEvaluator.shouldSkip(configClass)) {
    
    
			String beanName = configClass.getBeanName();
			if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
    
    
				this.registry.removeBeanDefinition(beanName);
			}
			this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
			return;
		}

		if (configClass.isImported()) {
    
    
			registerBeanDefinitionForImportedConfigurationClass(configClass);
		}
		for (BeanMethod beanMethod : configClass.getBeanMethods()) {
    
    
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}

		loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
		loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
	}

loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
Well, here is finally going to take the previously cached Map

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

Traverse each ImportBeanDefinitionRegistrar class, and call the registerBeanDefinitions method to complete subsequent registration work.

Guess you like

Origin blog.csdn.net/huangdi1309/article/details/121764438