To become a Spring bean, we usually add annotations @ Component, @ Controller, @ Service, @ Repository and other annotations on the class. And Spring from 4 Since the X version, Spring supports the Conditional annotation @ Conditional, and only the classes that meet the conditions can be registered as Spring beans. Therefore, before analyzing the source code, we can probably guess that there is such a process. First, Spring reads the metadata of all classes from the class path, and then selects the classes with specific annotations. Finally, if there are Conditional annotations, you need to meet the conditions to register as beans. Is that actually the case?
Scan process overview
We still analyze the scanning method of the AnnotationConfigApplicationContext application context as an entry. The source code is as follows:
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry { // Used to register bean s private final AnnotatedBeanDefinitionReader reader; // Used to scan and register bean s private final ClassPathBeanDefinitionScanner scanner; public AnnotationConfigApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } //Scan from a given package @Override public void scan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); this.scanner.scan(basePackages); } }
scan is a method of scanning packages. Its implementation is extremely simple. It is entrusted to the ClassPathBeanDefinitionScanner for scanning. This is a combination mode often used in Spring's internal implementation to make the responsibilities of classes clearer. View the source code of ClassPathBeanDefinitionScanner scanning as follows:
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider { public int scan(String... basePackages) { int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); // Scan and register bean s doScan(basePackages); // If necessary, register the bean factorypostprocessor that handles annotations if (this.includeAnnotationConfig) { AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); } }
Here, the scanning is divided into two steps: one is scanning, and the other is registering the processor beanfactoryprocessor configured to process annotations. The application context will call the beanfactoryprocessor method in its life cycle, which gives the Bean an opportunity for additional processing, For example, register the return object of the method annotated with @ Bean annotation in the class annotated with @ Configuration as a Bean in Spring. The registered processor and its implementation will be discussed later. Let's focus on the scanning process here. The source code of the tracking doScan method is as follows:
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) { // Loop to find candidate components from packages Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); // Gets or generates the name of the bean String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); // Reprocess the candidate components and set the necessary information if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } // Check whether the candidate component conflicts with the existing bean or needs to be registered if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); // Register bean registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
Spring's method is generally not too long, and the structure is relatively clear. Here we can see that spring first finds the candidate components, then sets the necessary properties for the candidate components, and finally checks the candidate components and the existing bean definitions. If there is no conflict, it will register. The registration uses a map to save the bean definitions, which will be analyzed later. Track the method findCandidateComponents for finding candidate components. The source code is as follows: