Spring source code analysis -- Bean registration

Keywords: Spring xml

There are two types of beans in Spring, one is a normal bean, the other is a factory bean:
Bean factory is a factory used to manage beans. All beans are managed by BeanFactory (i.e. IOC container)
FactoryBean is a factory Bean that can produce or decorate objects. Its implementation is similar to the factory pattern and the decorator pattern in the design pattern, eliminating the appearance of xml configuration factory pattern. Create a proxy class through FactoryBean to enhance the target class.

2, bean creation

Let's continue with the AbstractApplicationContext.refresh method

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();
 
			// Tell the subclass to refresh the internal bean factory.
			
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			
			prepareBeanFactory(beanFactory);
 
			try {
				// Allows post-processing of the bean factory in context subclasses.
				
				postProcessBeanFactory(beanFactory);
 
				// Invoke factory processors registered as beans in the context.
				
				invokeBeanFactoryPostProcessors(beanFactory);
 
				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);
 
				// Initialize message source for this context.
				initMessageSource();
 
				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();
 
				// Initialize other special beans in specific context subclasses.
				onRefresh();
 
				// Check for listener beans and register them.
				registerListeners();
 
				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);
 
				// Last step: publish corresponding event.
				finishRefresh();
			}
 
			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}
 
				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();
 
				// Reset 'active' flag.
				cancelRefresh(ex);
 
				// Propagate exception to caller.
				throw ex;
			}
 
			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

5,invokeBeanFactoryPostProcessors(beanFactory)

Traverse all implementation classes that implement the BeanFactoryPostProcessor interface

// Call beanFactory's postprocessor BeanDefinitionRegistryPostProcessor
// Users can implement beandefinitionregistrypostprocessor and postprocessbeandefinitionregistry to register beans
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
// User implemented BeanFactoryPostProcessor but not BeanDefinitionRegistryPostProcessor
regularPostProcessors.add(postProcessor);
}
}

Parsing the annotation completes the bean registration. Previously, only the beans in the XML are registered, but the annotated beans are not registered

// Call configurationclasspostprocessor? Postprocessbeandefinitionregistry to parse the annotation and register the bean
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

We need to know which annotations identify the bean to be handed over to spring management.

1,@Configuration+@Bean

The @ Con configuration annotation class indicates that its main purpose is to serve as the source of Bean definition. The @ Con configuration class allows the dependency between beans to be defined by calling other @ Bean methods in the same class.  

@Configuration 
public class AppConfig {
@Bean      
public MyService myService() {        
return new MyService();    
 }
 @Bean    
public UserService userService(){        
return new UserService(myService()); 
 }         
}

The @ Con "confi guration is configured, and the printing result shows the object myService relied on by UserService and the object myService obtained from the container;
The @ Con "confi guration is not configured. myService, the object that UserService depends on, is not obtained from the container, but a common object.

Because the AppCon g decorated by @ Con configuration is a cglib proxy object, @ Con configuration ensures that bean s are obtained from the container when dependent calls are made between internal methods of configuration classes

2,@ComponentScan+@Component, @Repository,@Service, @Controller 

I don't want to introduce this one by one if I use it more.

3,@Import

In the following example, myimportbeandefinitionregister:

If the ImportSelector interface is implemented, its selectImports method will be instantiated and called. According to this method, bean s can only be assembled according to types
If you implement the DeferredImportSelector, instantiate it and call its selectImports method, it will be later than the previous call,
If you implement the importbeandefinitionregister interface, instantiate and call its registerBeanDefinitions method, you can modify the information of the registered bean.
If it doesn't, it just instantiates the class

@Configuration 
@Import(value = MyImportBeanDefinitionRegistrar.class)
public class AppConfig {
 @Bean   
public User user(){      
return new User();   
} 
}

In the @ MapperScan annotation, @ import is used, so you can scan and parse mapper files

4,@Conditional

MyConditional will implement the matches method in the Condition interface and what conditions are met to assemble the bean

@Configuration 
public class AppConfig {
 @Bean 
 @Conditional(value = MyConditional.class) 
public User user(){      
return new User();   
} 
}

 

Returning to the invokebeadefinitionregistrypostprocessors method, you can see that the postProcessBeanDefinitionRegistry method that implements the BeanFactoryPostProcessor interface is called. Let's focus on the postprocessor of configuration class postprocessor parsing annotations.

private static void invokeBeanDefinitionRegistryPostProcessors(
			Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {

		// ConfigurationClassPostProcessor
		for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
			postProcessor.postProcessBeanDefinitionRegistry(registry);
		}
	}

Enter ConfigurationClassPostProcessor.processConfigBeanDefinitions to scan the annotations and register the bean.

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 {
			// Resolve configuration class @ ComponentScan (bean register to container) @ import @ importresource @ bean (the next three will not register to bean container)
			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());
			}
			// Register bean s to container
			//  Register the bean that implements the ImportSelector
			// Method bean register to container
			// @Beans configured by ImportResource("spring.xml") are registered in the container
			// Beans implementing import bean definitionregister register to container
			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());

First, the @ Configuration annotation will be scanned and parsed, parser.parse(candidates)

//In this parse method, all the DeferredImportSelector implementation classes are put into the deferredImportSelectors collection,
// Their selectImports method will not be executed, while the selectImports of other ImportSelector implementation classes will be executed
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());

It will call doProcessConfigurationClass(configClass, sourceClass)

Register @ Component decorated bean to container

//  Process @ ComponentScan to register @ Component decorated bean s to the container
		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
				// @ComponentScan scans beans, returns the @ Component decorated BeanDefinitionHolder collection, and
				// Will register the bean to the container
				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
				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());
					}
				}
			}
		}

For @ Import annotation processing, if the class implementing the ImportSelector interface is not registered in the bean at this time, the first processConfigBeanDefinitions calls this.reader.loadBeanDefinitions(configClasses) to register

// Processing @ import imports importselector does not register bean s with the container
processImports(configClass, sourceClass, getImports(sourceClass), true);

Now all the bean information has been registered in the map collection of DefaultListableBeanFactory

Published 35 original articles, won praise 52, visited 40000+
Private letter follow

Posted by ManicMax on Wed, 04 Mar 2020 01:41:39 -0800