Do you know how Spring resolves configuration classes?

Keywords: Spring Java xml Maven

Do you know how Spring resolves configuration classes?


Second line of code in Spring
ClassPathBeanDefinitionScanner Source Analysis
1. Complete the scan by findCandidateComponents method
2. Execute the postProcessBeanDefinition method
3. Execute the processCommonDefinitionAnnotations method
4. Register BeanDefinition
How does Spring resolve configuration classes?
1. Analysis of Parsing Timing
2. Parse Source Code Analysis
Understand Spring (2) Do you know how Spring parses configuration classes?

Recommended reading:

Spring Official Reading Series

Read the source code thoroughly. We can start with the first line

The Spring execution flowchart is as follows:

If the picture is not clearly displayed, you can visit the following link to view the large HD image:

Spring Execution Flowchart

This flowchart will become more detailed and complex as we learn, and hopefully we will be able to make progress towards our Spring proficiency goal in the process!

In the previous article, we learned the first line of code in Spring, and we already know that the first line of code in Spring is the creation of an AnnotatedBeanDefinitionReader object whose primary purpose is to register bd (BeanDefinition) into a container.During the creation of this object, Spring also registered several BDS that were pioneering for containers, including ConfigurationClassPostProcessor, AutowiredAnnotationBeanPostProcessor, and so on.

So in this article, let's take a look at what the second line of code in Spring does?

Second line of code in Spring
The second line of code is clearly labeled in the flowchart above and is

this.scanner = new ClassPathBeanDefinitionScanner(this);
It simply creates a ClassPathBeanDefinitionScanner object.So what does this ClassPathBeanDefinitionScanner do?It looks like this object by name to complete the scan in Spring, is that really true?I hope you can look down with these two questions

ClassPathBeanDefinitionScanner Source Analysis
This class name is translated literally as BeanDefinition's scanner in the class path, so we are directly concerned with its scanning-related method, which is the doScan method.The code is as follows:

//This method completes the scan of the class file under the specified package name
// basePackages: Specifies the package name, which is a variable parameter
protected Set doScan(String... basePackages) {

Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {    
    // 1.findCandidateComponents This method is the actual way to complete the scan and is the method we will analyze next.
    Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
    for (BeanDefinition candidate : candidates) {,
        // We've already analyzed it in the last article and finished parsing the @Scope comment
        // Refer to "Understand Spring (1) Read the source code thoroughly, we can start from the first line"
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
        String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
        if (candidate instanceof AbstractBeanDefinition) {
    // 2. If you know something about BeanDefinition, you will know that this judgment will be valid, which means that //all scanned BDS will perform some postProcessBeanDefinition post-processing      
            postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
        if (candidate instanceof AnnotatedBeanDefinition) {  
    // 3. Is it an AnnotatedBeanDefinition, and if so, additional processing is required 
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
       // 4. Check if the bd already exists in the container and if so, do not register it                                 
        if (checkCandidate(beanName, candidate)) {
            // The following logic was analyzed in the previous article and skipped here
            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
            definitionHolder =
                AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            registerBeanDefinition(definitionHolder, this.registry);
return beanDefinitions;

There are four main things this code does

Complete the scan using the findCandidateComponents method
Determine if the scanned bd is an AbstractBeanDefinition and, if so, execute the postProcessBeanDefinition method
Determine if the scanned bd is an AnnotatedBeanDefinition and, if so, execute the processCommonDefinitionAnnotations method
Check to see if the bd already exists in the container and if so do not register
Next, let's step through this approach and see what the ClassPathBeanDefinitionScanner really does.

1. Complete the scan by findCandidateComponents method
The findCandidateComponents method source code is as follows:

public Set findCandidateComponents(String basePackage) {

if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
    return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
else {
    // Normally this judgment is entered and the class file under classpath is scanned
    return scanCandidateComponents(basePackage);

Don't focus too much on this approach.Spring normally scans class files under classpath, but although class path-based scanning is fast, creating static lists of candidates at compile time can improve startup performance for large applications.In this mode, all modules of the application must use this mechanism because when the ApplicationContext detects such an index, it will automatically use it instead of scanning the class path.
To generate an index, simply add additional dependencies to each module that contains the component scan instructions target component:

org.springframework spring-context-indexer 5.0.6.RELEASE true If you are interested, you can refer to the official website:

This dependency is too big to be pulled down for half a day. I will not show it here.

Normally our applications are scanned in this way, and the code is as follows:

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    // The collection used to store the returned bd
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        // Split into this form: classpath*:com.dmz.spring
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        // Get the Resource object encapsulated by all the class files
        Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);

        // Resource object encapsulated from all the traversed class files
        for (Resource resource : resources) {
            if (traceEnabled) {
                logger.trace("Scanning " + resource);
            if (resource.isReadable()) {
                try {
                    // Build a MetadataReader object from Resource that contains meta-information and annotation meta-information about the classes parsed out of the corresponding class file
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                    // Not all class file files need to be parsed into bd, only annotated (@Component,@Controller, etc.) are components in Spring
                    if (isCandidateComponent(metadataReader)) {
                        // Parse meta information (class meta information and annotation meta information) to get a ScannedGenericBeanDefinition
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        if (isCandidateComponent(sbd)) {
                            if (debugEnabled) {
                                logger.debug("Identified candidate component class: " + resource);
    // Omit redundant code
    return candidates;

I have drawn such a picture in the article "Containers and instantiations" on Spring's website

As you can see from the diagram above, java class + configuration metadata will eventually be converted to a BenaDefinition, which, combined with the code analysis above, means that java class + configuration metadata is actually a MetadataReader object, whereas converting to a BenaDefinition means creating a ScannedGenericBeanDefinition from this MetadataReader object.

2. Execute the postProcessBeanDefinition method
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {

// Set default values for properties in bd

// This value must be null in annotation mode, when using XML configuration,
if (this.autowireCandidatePatterns != null) {
    beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));


//Set default value

public void applyDefaults(BeanDefinitionDefaults defaults) {


As you can see, the main function of the postProcessBeanDefinition method is to set default values for the scanned bd to further populate the properties in the bd

3. Execute the processCommonDefinitionAnnotations method
This code further parses the annotation information on the class into which Spring placed the current class when it created this abd's information. All this code does is get the annotation above through the class object (including @Lazy, @Primary, @DependsOn annotation, etc.), and then get the corresponding configuration information from the annotation and put it into the properties in the bd

4. Register BeanDefinition
The registration logic that we can read from the first line is the same as reading the source code thoroughly

From the above analysis, we have known the role of ClassPathBeanDefinitionScanner. There is no doubt that Spring must complete the scan through this class, but the question is, does Spring complete the scan through this object created in the second step?Let's take a look at the creation of this ClassPathBeanDefinitionScanner:

//Step 1
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {

this(registry, true);

//Step 2
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {

this(registry, useDefaultFilters, getOrCreateEnvironment(registry));

//Step 3
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,

                                  Environment environment) {

this(registry, useDefaultFilters, environment,
     (registry instanceof ResourceLoader ? (ResourceLoader) registry : null));

//Step 4
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,

                                  Environment environment, @Nullable ResourceLoader resourceLoader) {

Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;

if (useDefaultFilters) {
    // Register Default scan filtering rules (to be modified by the @Component annotation)

During the creation of this ClassPathBeanDefinitionScanner, we were not able to interfere with the whole process and we could not configure any of the ClassPathBeanDefinitionScanner.And we know in the configuration class that we can configure rules for scanning, for example:

@ComponentScan(value = "", useDefaultFilters = true,

           excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {IndexService.class}))

So this ClassPathBeanDefinitionScanner object created here is definitely not used in Spring.

The actual time to complete the scan is in steps 3-5-1 of our flowchart.The class that completes the scan function is the ConfigurationClassPostProcessor mentioned in the previous article.Next, we'll look at how Spring actually does the scanning, which is the main point of this article

How does Spring resolve configuration classes?
1. Analysis of Parsing Timing
What did Spring do before parsing?
Register Configuration Class
Before analyzing the scan timing, let's review the previous code, and the entry to the whole program is as follows:

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {


In this() null parameter construction, Spring instantiates two objects, one is AnnotatedBeanDefinitionReader, which was described in the previous article, and the other is ClassPathBeanDefinitionScanner, which is also analyzed in detail in the previous article.

After completing the creation of these two objects, Spring then registers the configuration class with the container using the AnnotatedBeanDefinitionReader created in the first step.Seeing this, I don't know if you have a question. Since Spring registers configuration classes directly this way, why don't we add the @Configuration annotation to the configuration classes?With this code, I can register a configuration class into a container without adding any annotations to it, such as the following:

public class Config {

public class Main {

public static void main(String[] args) throws Exception {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
    // Program output:
    // This means that Config is registered in the container

Now that you think about this question carefully, you might as well continue to look down with these questions.

Call refresh method
After registering the configuration class with the container, Spring then calls the refresh method with the following source code:

public void refresh() throws BeansException, IllegalStateException {

synchronized (this.startupShutdownMonitor) {
    // This method mainly does the following things
    // 1. Record the startup time of the container and change the container state to active
    // 2. Call the initPropertySources() method, which is mainly used to initialize encapsulation of related web resources in the web environment, such as encapsulating the servletContext as ServletContextPropertySource
    // 3. Verify the existence of necessary attributes in the environment
    // 4. An extension point is provided to pre-populate events, which are published directly when the application EventMulticaster bean s are registered in the container

    // What you actually get is a DefaultListableBeanFactory
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    // Set some properties for the bean factory

    try {
        // Provides methods for subclass replication, allowing subclasses to do some postprocessing of beanFactory at this step

        // Performs a scan performed here by the post processor of the bean factory registered in the container

        // The following code is not related to scans, but we'll cover it in a later article
    // .....

Most of the code is commented in great detail, and we'll analyze two of the more complex methods separately

What did prepareBeanFactory do?
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {

// Set up a classLoader, usually appClassLoader
// Set el expression parser
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
// A property editor registry has been added to the container, and the BeanWrapper and Type Conversion for Property Editing in Spring's Official Reading (14) Spring are described in more detail here.
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

//    A bean's postprocessor was added to execute the xxxAware method
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

// Under the auto-injection model, do not inject if the following types of dependencies exist in the bean s

// Why can we inject objects such as ApplicationContext directly into bean s?That's what this code does!
// When Spring injects attributes, it looks in the map of resolvableDependencies for the existence of a corresponding type of beans, if any, and injects them directly. The following code places the corresponding beans in the map of resolvableDependencies
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);

// Add a postprocessor to handle the ApplicationListener
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

// Whether LTW is configured, that is, weaving during class loading, is not typically configured
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
    beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
    // Weaving during loading configures a temporary class loader
    beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));

// Configure some default environment-related bean s
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
    beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
    beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
    beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());

The code above is very simple as a whole, and the logic is clear, that is, to do some configuration for the beanFactory, what we need to be aware of is the content related to the rear processor. You can see that there are two post processors registered at this step.

ApplicationContextAwareProcessor, used to execute methods in the xxxAware interface
ApplicationListenerDetector to ensure that listeners are added to the container
For ApplicationListenerDetector, refer to Spring's official website Read (8) Extension Points for Containers (3) (BeanPostProcessor)

What has invokeBeanFactoryPostProcessors done?
The execution process of this method has been analyzed in great detail in Spring's official website (6) Extension Points of Containers (1) BeanFactoryPostProcessor, whose execution process is as follows

Integrally, it executes the post-processor of a registered bean factory in a container in a certain order.

So what are the bean factory's back processors in the container so far?

Remember the ConfigurationClassPostProcessor we mentioned in the previous article?During the creation of AnnotatedBeanDefinitionReader, its corresponding BeanDefinition was registered in the container.Next let's analyze the source code for the ConfigurationClassPostProcessor class

ConfigurationClassPostProcessor Source Analysis
It implements BeanDefinitionRegistryPostProcessor, so first execute its postProcessBeanDefinitionRegistry method with the following source code

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {

   // Generate a registry ID
int registryId = System.identityHashCode(registry);
// Indicates that the factory has gone through a back-end processor
// By name, this method handles the bd of the configuration class again

The processConfigBeanDefinitions method is very long code, so let's split up a section of analysis and look at the first section first

First paragraph
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {

// =========First piece of code=========
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();

// You can think about a question, which BeanDefinition s are in the current container?
// What names should I get here?
String[] candidateNames = registry.getBeanDefinitionNames();

for (String beanName : candidateNames) {
    // Get the corresponding BeanDefinition by name
    BeanDefinition beanDef = registry.getBeanDefinition(beanName);
    // Omit log printing
    // Check if it is a configuration class, where the corresponding bd is marked as FullConfiguration Class or LiteConfiguration Class
    else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
        // If it is a configuration class, add this bd to configCandidates
        configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));

// No configuration class, return directly
if (configCandidates.isEmpty()) {

// Sort by @Order comment
configCandidates.sort((bd1, bd2) -> {
    int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
    int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
    return, i2);
// .....

The above code has several problems:

Which BeanDefinition s are in the current container
If you've read the previous article, you should know that Spring registered five BeanDefinitions with the container when it created the AnnotatedBeanDefinitionReader object, plus the registered configuration classes, there should be six BeanDefinitions in the container at this point, so we can break point validation

Unexpectedly, there are six

The code is as follows:

public static boolean checkConfigurationClassCandidate(
        BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {

    String className = beanDef.getBeanClassName();
    if (className == null || beanDef.getFactoryMethodName() != null) {
        return false;
    // This section below is all about getting an AnnotationMetadata
    // AnnotationMetadata contains annotation meta-information and class meta-information on the corresponding class
    AnnotationMetadata metadata;
    if (beanDef instanceof AnnotatedBeanDefinition &&
            className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
        // Parsed, such as registered configuration classes, obtained directly from bd
        metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
    else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
        // Get the byte code and re-parse to get an AnnotationMetadata
        Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
        metadata = new StandardAnnotationMetadata(beanClass, true);
    else {
        try {
            // Without the class attribute, this AnnotationMetadata is obtained from the className using ASM bytecode Technology
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
            metadata = metadataReader.getAnnotationMetadata();
        catch (IOException ex) {
            return false;

    // If marked with the @Configuration annotation, it means a FullConfiguration Candidate
    if (isFullConfigurationCandidate(metadata)) {
    // If marked by these annotations, @Component, @ComponentScan, @Import, @ImportResource
    // Or if there is a @Bean comment on the method, it's a LiteConfiguration Candidate
    // That is, you want to use this class as a configuration class without adding the @Configuration annotation
    else if (isLiteConfigurationCandidate(metadata)) {
    else {
        return false;
    // Resolve the @Order comment for sorting
    Integer order = getOrder(metadata);
    if (order != null) {
        beanDef.setAttribute(ORDER_ATTRIBUTE, order);

    return true;

The second paragraph
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {

// First paragraph
// .....
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
    sbr = (SingletonBeanRegistry) registry;
    // Generation strategy for beanName, not important
    if (!this.localBeanNameGeneratorSet) {
        BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
        if (generator != null) {
            this.componentScanBeanNameGenerator = generator;
            this.importBeanNameGenerator = generator;

if (this.environment == null) {
    this.environment = new StandardEnvironment();
// The core purpose is to create this ConfigurationClassParser object
ConfigurationClassParser parser = new ConfigurationClassParser(
    this.metadataReaderFactory, this.problemReporter, this.environment,
    this.resourceLoader, this.componentScanBeanNameGenerator, registry);

//Paragraph 3
The core purpose of this code is to create a ConfigurationClassParser, which is used for subsequent configuration class resolution.

Paragraph 3
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {

// First paragraph, second paragraph
// .....
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
    // In the second section of code, a ConfigurationClassParser is created, which is used here to resolve the configuration class
    // We know that the scan is done by @ComponentScan, @ComponentScans, so it's not surprising that the scan must be done here
    // Checks for errors in the parsing process and whether @Bean methods in classes annotated by @Configuration can be overridden (neither final ized nor private ly accessed) and throws exceptions if they cannot be overridden, since the cglib agent completes the proxy by overriding the parent class, which is described in more detail later
    // Configuration classes that have been resolved
    Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
    // Remove the parsed configuration class to prevent duplicate loading of bd in the configuration class

    // 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());
    // By parsing the @Bean, @Import and other annotations, the relevant information will be parsed into bd and injected into the container

    // If it is larger, indicating that some new BDS have been added to the container, then it needs to be re-determined if the new bd is a configuration class, and if it is a configuration class, it needs to be resolved again
    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) {
        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());
// Register ImportRegistry in Container
// When a fully configured class A (a class decorated with the @Configuration annotation) is imported through the @Import annotation, A implements the ImportAware interface
// This Aware lets you know which class imported A
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
    sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());

if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
    ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();

2. Parse Source Code Analysis
In the source analysis above, we have been able to determine that Spring is parsed through the Configuration Class Parser's parse method to complete the parsing of the configuration class.Spring is very sophisticated in naming classes, and ConfigurationClassParser translates directly to configuring class parsers.Then let's look at its source code

2.1. parse method
public void parse(Set configCandidates) {

this.deferredImportSelectors = new LinkedList<>();

// Traverse through all configuration classes, one by one to complete the parsing
for (BeanDefinitionHolder holder : configCandidates) {
    BeanDefinition bd = holder.getBeanDefinition();
    try {
        // All three judgments end up in the same method-->processConfigurationClass method
        if (bd instanceof AnnotatedBeanDefinition) {
            parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
        else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
            parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
        else {
            parse(bd.getBeanClassName(), holder.getBeanName());
    catch (BeanDefinitionStoreException ex) {
        throw ex;
    catch (Throwable ex) {
        throw new BeanDefinitionStoreException(
            "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
// Delayed processing of ImportSelector


2.2. ProceConfigurationClass method

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
    // Resolve the @Conditional comment to determine if it needs to be resolved
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {

    // Determine if the parser has parsed this configuration class
    ConfigurationClass existingClass = this.configurationClasses.get(configClass);
    // Not null, description has been parsed
    if (existingClass != null) {
        // If the configuration class to be resolved was imported by the @Import annotation
        if (configClass.isImported()) {
            // And the parsed configuration class is also imported
            if (existingClass.isImported()) {
                // Then the import class of the current configuration class is added to the import class set of this configuration class. (A imports B through @Import, then A is the import class of B, and B is imported by A)
            // Otherwise ignore new imported config class; existing non-imported class overrides it.
            // If the resolved configuration class is not imported, the new imported configuration class is directly ignored.That is, if a configuration class is also imported by @Import and is normal
            // If added to a container, the configuration classes normally added to the container will overwrite the imported classes
        else {
            // Explicit bean definition found, probably replacing an import.
            // Let's remove the old one and go with the new one.
            // That is, the new configuration class to be resolved is not imported, so in this case, remove the configuration class that calls the original resolution directly
            // Why not remove(existingClass)?You can see the hashCode and equals methods
            // remove(existingClass) is equivalent to remove(configClass)

    // Recursively process the configuration class and its superclass hierarchy.
    // The following code is mainly a recursive processing configuration class and its parent
    //  Encapsulating a configuration class into a SourceClass facilitates unified processing
    SourceClass sourceClass = asSourceClass(configClass);
    do {
        // The doxxx method, the really working method, handles the configuration class, and the return value is the parent of the current class
        sourceClass = doProcessConfigurationClass(configClass, sourceClass);
    while (sourceClass != null);

    this.configurationClasses.put(configClass, configClass);

2.3, doProcessConfigurationClass method
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)

throws IOException {

// Recursively process any member (nested) classes first
// Recursive processing of internal classes
processMemberClasses(configClass, sourceClass);

// Process any @PropertySource annotations
// Processing the @PropertySources and @PropertySource annotations adds the corresponding property resources to the container (actually to the environment)
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
    sourceClass.getMetadata(), PropertySources.class,
    org.springframework.context.annotation.PropertySource.class)) {
    if (this.environment instanceof ConfigurableEnvironment) {
    else {
        logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                    "]. Reason: Environment must implement ConfigurableEnvironment");

// Process any @ComponentScan annotations,
// Processing the @ComponentScan, @ComponentScans annotation, the real place to scan is here
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
            // Core code, scan done here
            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
            // Check if the scanned bd is a configuration class and if it is recursively resolved
            for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                // Normally getOriginatingBeanDefinition gets null
                // When isn't null?, Reference: ScopedProxyUtils.createScopedProxy method
                // Will not be null when creating a proxy bd
                BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                if (bdCand == null) {
                    bdCand = holder.getBeanDefinition();
                // Determine if the scanned bd is a configuration class and continue processing recursively if so
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                    parse(bdCand.getBeanClassName(), holder.getBeanName());
// Process any @Import annotations
// Handle @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);

// Process any @ImportResource annotations
// Handle @ImportResource annotations
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);

// Process individual @Bean methods
// Handle @Bean annotations
// Get the method labeled by @Bean
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
    // Add to configClass
    configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));

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

// Process superclass, if any
// Return to parent class for recursive processing
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;

You can see that the doProcessConfigurationClass has actually finished parsing the configuration class, doing the following things together

Resolve the internal classes in the configuration class to see if there are any configuration classes in the internal class and if there are recursive processing
Processing @PropertySources and @PropertySource annotations on configuration classes
Processing @ComponentScan, @ComponentScans comment
Handle @Import annotations
Handle @ImportResource annotations
Handle @Bean annotations
Handling default methods in interfaces
Returns the parent class, allowing the external loop to continue processing the parent of the current configuration class
We analyzed one by one

2.4. Processing internal classes in configuration classes
This code is very simple, but for space reasons I'm not going to go into it here. I'm going to get all the internal classes in the current configuration class, and then go through all the internal classes to see if it's a configuration class or if it's a configuration class, I'll parse it recursively

2.5, Processing @PropertySource annotations
The code is also very simple, loading the corresponding property file based on the information in the comment and adding it to the container

2.6, Processing @ComponentScan annotations
In this section, we need to look at the scans Spring has done here, and we'll look directly at its core methodology, org.springframework.context.annotation.ComponentScanAnnotationParser#parse

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
    // The first step creates a ClassPathBeanDefinitionScanner object
    // Here we know that Spring scans without using the object that was created at the beginning
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
            componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
    // Generation rules for beanName used when parsing to bd
    Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
    boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
    scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
    // Configure default values for ScopedProxyMode under this scanning rule
    ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
    if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
    else {
        Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
    // Configure Scanner Matching Rules

    // Configure the components scanner needs to scan
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
        for (TypeFilter typeFilter : typeFiltersFor(filter)) {

    // Configure components that the scanner does not need to scan
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
        for (TypeFilter typeFilter : typeFiltersFor(filter)) {

    // Configure whether lazy loading is the default
    boolean lazyInit = componentScan.getBoolean("lazyInit");
    if (lazyInit) {

    // Configure the package name scanner scans
    Set<String> basePackages = new LinkedHashSet<>();
    String[] basePackagesArray = componentScan.getStringArray("basePackages");
    for (String pkg : basePackagesArray) {
        String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
        Collections.addAll(basePackages, tokenized);
    for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
    if (basePackages.isEmpty()) {

    // Exclude yourself
    scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
        protected boolean matchClassName(String className) {
            return declaringClass.equals(className);
    // After configuring the scanner, call its doScan method directly to scan
    return scanner.doScan(StringUtils.toStringArray(basePackages));

See, the first step is to create a ClassPathBeanDefinitionScanner, then configure the scanner by parsing the annotations, then call the doScan method to complete the scan.

2.7, Processing @Import annotations

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
        Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
    // No classes to import, return directly
    if (importCandidates.isEmpty()) {
    // checkForCircularImports: Write-to-death is true in Spring, need to check cycle import
    // isChainedImportOnStack method: Check to see if this configClass exists in the import stack, if a description exists
    // A import B,B import A occurred, throwing an exception directly
    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
        this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    else {
        // There is no circular import, first add the current configuration class to the import stack
        try {
            // Traverse through all classes to be imported
            for (SourceClass candidate : importCandidates) {
                // If the class to be imported is an ImportSelector
                if (candidate.isAssignable(ImportSelector.class)) {
                    // Candidate class is an ImportSelector -> delegate to it to determine imports
                    // Reflection creates this ImportSelector
                    Class<?> candidateClass = candidate.loadClass();
                    ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                    // Execute xxAware method
                            selector, this.environment, this.resourceLoader, this.registry);
                    // If it is a DeferredImportSelector, add it to the deferredImportSelectors collection
                    // Processing ImportSelector in deferredImportSelectors collection after all configuration classes have been resolved
                    if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
                                new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
                    else {
                        // Not a DeferredImportSelector, then use this ImportSelector to get the class name to import
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                        // Convert it to SourceClass
                        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                        // Recursive processing of classes to be imported typically involves two other decisions
                        processImports(configClass, currentSourceClass, importSourceClasses, false);
                else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                    // Candidate class is an ImportBeanDefinitionRegistrar ->
                    // delegate to it to register additional bean definitions
                    // If it is an ImportBeanDefinitionRegistrar
                    // Create this ImportBeanDefinitionRegistrar by reflection first
                    Class<?> candidateClass = candidate.loadClass();
                    ImportBeanDefinitionRegistrar registrar =
                            BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
                    // Execute the xxAware method again
                            registrar, this.environment, this.resourceLoader, this.registry);
                    // Finally, add it to the importBeanDefinitionRegistrars collection of configClass
                    // The registerBeanDefinitions method of its ImportBeanDefinitionRegistrar is then called uniformly, registering the corresponding bd in the container
                    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                else {
                    // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
                    // process it as an @Configuration class
                    // Neither an ImportSelector nor an ImportBeanDefinitionRegistrar, import a common class directly
                    // Recursively process this class as a configuration class
                            currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to process import candidates for configuration class [" +
                    configClass.getMetadata().getClassName() + "]", ex);
        finally {
            // We add it to the import stack before the loop, and pop it up when the loop is finished, mainly to handle the loop import

2.8, Processing @ImportResource Comments
The code is also simple, loading the resource at the specified location and adding it to the configClass.Typically, what we import through the @ImportResource annotation is an XML configuration file.After adding this Resource to the configClass, Spring parses the XML configuration file later and registers the bd in the container, referring to the org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions method

2.9, Processing @Bean annotations
Add all @Bean-labeled methods in the configuration class to the BeanMethod collection of the configClass

2.10. Processing default methods in interfaces
The code is also simple, and the interface in Java8 defines the default method, which is how the default method in the interface is handled to see if it has a @Bean label

So far, we have analyzed the whole process of parsing.You can see that Spring stores all the parsed configuration information in the ConfigurationClass class, but so far the stored information has not been used.So where does Spring use this information?Back to our third paragraph of code, there is one line of code shown in the figure:

That is, here Spring completes the information processing for the parsed configuration class.

2.11. Configuration information after loading and parsing
// configurationModel: The set of configuration classes has been parsed, where @Bean annotation parsing information, @Import annotation parsing information and so on are saved.
public void loadBeanDefinitions(Set configurationModel) {

TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
    // Load completed by calling this method
    loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);

private void loadBeanDefinitionsForConfigurationClass(

ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
// To determine if skipping is required, such as when A imports B, A needs to be skipped if it does not meet the loading criteria, then B should also be skipped
if (trackedConditionEvaluator.shouldSkip(configClass)) {
    String beanName = configClass.getBeanName();
    if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {

// To determine if the configuration class was imported, the actual code is to determine if the importedBy collection in the resolved configclass is empty
// So what does this importedBy collection do?
// For example, if A imports B through @Import, then parsing B yields the importedBy set in configclass that contains A
// In short, the importedBy collection is another class that imports this class (possibly multiple classes at the same time)
// As we've already analyzed in the previous section, when multiple classes are imported at the same time, the mergeImportedBy method is called to add an element to the collection
if (configClass.isImported()) {
// Resolve the Method of the @Bean label to get the corresponding BeanDefinition and register it in the container
for (BeanMethod beanMethod : configClass.getBeanMethods()) {

// Parse the imported configuration file and register the bd from it into the container

// Execute the registerBeanDefinitions method of all ImportBeanDefinitionRegistrar s in configClass

This code is still very simple to read, so I'll take a look at the BeanMethod code with you, mainly to give you a deeper understanding of BeanDefinition. The source code is as follows:

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
    ConfigurationClass configClass = beanMethod.getConfigurationClass();
    MethodMetadata metadata = beanMethod.getMetadata();
    String methodName = metadata.getMethodName();

    // Judging whether skipping is necessary based on the @Conditional comment
    if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
    if (configClass.skippedBeanMethods.contains(methodName)) {
    // Get the properties in the @Bean comment
    AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
    Assert.state(bean != null, "No @Bean annotation attributes");

    // As you can see here, if no beanName is configured, the method name is taken by default as the beanName
    List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
    String beanName = (!names.isEmpty() ? names.remove(0) : methodName);

    // Register Alias
    for (String alias : names) {
        this.registry.registerAlias(beanName, alias);

    // isOverriddenByExistingDefinition This method determines if the currently registered bd is overwritten by an existing bd
    // What is coverage?We will analyze in detail later
    if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
        // Satisfying the if below means that @Bean creates a bean with the same name as the configuration class in which the @Bean labeled method resides, in which case an exception is thrown directly
        if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {
            throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
                    beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() +
                    "' clashes with bean name for containing configuration class; please make those names unique!");
    // Create a ConfigurationClassBeanDefinition, from which you can see that beans created with @Bean have BDS corresponding to ConfigurationClassBeanDefinition
    ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
    beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
    // @Bean is static, so you only need to know the class name and method name of the static method to execute it
    if (metadata.isStatic()) {
        // static @Bean method
    else {
        // instance @Bean method
    // The next code is to set some properties of the bd, then register the BD with the container, and the related source code has been analyzed in the previous article
    // I'm not analyzing it here. Refer to the article "Read Source, we can start from the first line" recommended for reading.

The main purpose of this method above is to parse the @Bean labeled method into a BeandDefinition and register it with the container.For this method, we can compare the previous analysis of the org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean method.Comparing these two methods, we can find that one is based on Class object and the other is based on Method object.

Because of this, one big difference between them is the setting of the BeanClasss property in BeanDefinition.You can see that for beans created in the @Bean form, the BeanDefinition does not have the BeanClasss property set, but additional properties are set

Under the static method, the BeanClassName and FactoryMethodName properties are set, where BeanClassName is the class name of the static method and FactoryMethodName is the method name of the static method.
Under the instance method, the FactoryBeanName and FactoryMethodName properties are set, where FactoryBeanName is the name of the Bean corresponding to the instance and FactoryMethodName is the method name corresponding to the instance.
The BeanClasss property is not set because a Bean can be uniquely identified either by a specified static method or by a method in a specified instance.

In addition, when registering BeanDefinition as @Bean, a judgment is made on the isOverriddenByExistingDefinition(beanMethod, beanName) method, whose main purpose is to determine whether the beans currently being registered are overwritten by beans that previously existed.However, this judgment is not made when registering beans directly through the AnnotatedBeanDefinitionReader#doRegisterBean method, and if it exists, it will be overwritten directly instead of using the previous BD to overwrite the BD to be registered now.Why is that?According to the author's own understanding, because Spring also classifies beans into 369 classes, the BD obtained by @Bean can override the scanned normal bd(ScannedGenericBeanDefinition).However, the configuration class cannot be overwritten, so if the existing BD is a ScannedGenericBeanDefinition, it can be overwritten directly, but if the existing BD is a configuration class, it cannot be overwritten. To overwrite this registered bd, use the existing BD.

So far, we have completed the source analysis for the whole configuration class resolution and registration in Spring, but we have not finished yet. One problem we have to solve is why we need to add the @Configuration annotation on the configuration class. In the previous source analysis, we know that the purpose of adding the @Configuration annotation is to explain that the configuration class flag is a full one.ConfigurationClass, what is the purpose of this?Originally, I planned to finish one article, but it was really too long, nearly 6w words, so I split it into two articles. See below for future information:
Why do configuration classes add the @Configuration annotation?

By reading the source code thoroughly in conjunction with the previous article, we can start with the first line and sort out the current Spring execution process.

Original Map Address: Original Map

With a clear understanding of the process, let's go back and think about what postProcessBeanDefinitionRegistry did.

It's not easy. Remember to compliment your helpful words and pay attention to the waves. Thanks a lot!

Author: Programmer DMZ

Original Address

Posted by bigger on Sun, 10 May 2020 18:13:22 -0700