Spring 5 source code parsing 5-configuration classpostprocessor

Keywords: Java Spring Attribute github

Next, we talked about the invokeBeanFactoryPostProcessors(beanFactory) method in the refresh() method, which mainly executes the BeanFactoryPostProcessor and its sub interface BeanDefinitionRegistryPostProcessor.

When creating the annotationconfigpplicationcontext object, Spring adds a very important BeanFactoryPostProcessor interface implementation class: ConfigurationClassPostProcessor. Note that the addition mentioned here is only added to the beanDefinitionMap of the container, and the real instance Bean has not been created yet.

Briefly review when the ConfigurationClassPostProcessor was added to the container: when the AnnotatedBeanDefinitionReader object is created in the parameterless constructor of the annotationconfiguapplicationcontext, the BeanDefinition of the processors related to the parsing annotation configuration class will be registered in the BeanDefinitionRegistry passed in. Here is the ConfigurationClassPostProcessor. Added to the container.

ConfigurationClassPostProcessor

First look at the inheritance system of some configurationclasspostprocessors:

ConfigurationClassPostProcessor implements the BeanDefinitionRegistryPostProcessor interface, and has the ability to register BeanDefinition in the container when the Spring container is started.

As we know, the configurationclasspostprocessor ᦇ postprocessbeandefinitionregistry method is executed in invokeBeanFactoryPostProcessors(beanFactory); in the refresh(); method. Let's take a look at this method.

ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    int registryId = System.identityHashCode(registry);
    if (this.registriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException(
                "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
    }
    if (this.factoriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException(
                "postProcessBeanFactory already called on this post-processor against " + registry);
    }
    this.registriesPostProcessed.add(registryId);

    processConfigBeanDefinitions(registry);
}

The main logic is in processConfigBeanDefinitions(registry); click Open Source:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    //Get all beandefinitionnames
    String[] candidateNames = registry.getBeanDefinitionNames();

    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);

        // https://docs.spring.io/spring/docs/5.1.8.RELEASE/spring-framework-reference/core.html#beans-java-basic-concepts
        // Full @Configuration vs "lite" @Bean mode
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
                ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
            }
        }

        // Verify whether it is a configuration class
        // Configuration classes are divided into two types: Full @Configuration vs "lite" @ Bean mode
        // Add flag attribute in BeanDefinition after verification
        // Join configCandidates if the conditions are met
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            // If it's a configuration class, put it in the configCandidates variable.
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }

    // Return immediately if no @Configuration classes were found
    if (configCandidates.isEmpty()) {
        return;
    }

    // Sort by previously determined @Order value, if applicable
    configCandidates.sort((bd1, bd2) -> {
        int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
        int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
        return Integer.compare(i1, i2);
    });

    // Detect any custom bean name generation strategy supplied through the enclosing application context
    SingletonBeanRegistry sbr = null;
    // The incoming registry is DefaultListableBeanFactory
    if (registry instanceof SingletonBeanRegistry) {
        sbr = (SingletonBeanRegistry) registry;
        if (!this.localBeanNameGeneratorSet) {
            //Get the custom BeanNameGenerator, which is generally empty
            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();
    }

    // Parse each @Configuration class
    // new ConfigurationClassParser, used to parse @ Configuration class
    ConfigurationClassParser parser = new ConfigurationClassParser(
            this.metadataReaderFactory, this.problemReporter, this.environment,
            this.resourceLoader, this.componentScanBeanNameGenerator, registry);

    // Convert configCandidates to set candidates and de duplicate
    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
    do {
        // Parsing configuration class
        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());
        }
        // Import class, @ Bean,@ImportResource is converted to BeanDefinition
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);

        candidates.clear();
        // Get the data of BeanDefinition in the container again. If the number of discoveries increases, it means that a new BeanDefinition has been registered.
        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());

    // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
    if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
        sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
    }

    if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
        // Clear cache in externally provided MetadataReaderFactory; this is a no-op
        // for a shared cache since it'll be cleared by the ApplicationContext.
        ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
    }
}

Get all BeanDefinitionNames, and then loop the array to determine whether it is a configuration class.

The first five are built-in processor s registered by Spring, and the last one is the configuration class AppConfig.class passed in to annotationconfigpplicationcontext.

There are two configuration classes in Spring, one is FullConfigurationClass and the other is LiteConfigurationClass. For the difference between the two, please refer to the previous articles about Full @Configuration and lite @Bean mode.

Configurationclassutils ා checkconfigurationclasscandidate method is to determine which configuration class it belongs to and mark the result in BeanDefinition. Its concrete judgment logic is as follows:

public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
    return metadata.isAnnotated(Configuration.class.getName());
}
private static final Set<String> candidateIndicators = new HashSet<>(8);

static {
    candidateIndicators.add(Component.class.getName());
    candidateIndicators.add(ComponentScan.class.getName());
    candidateIndicators.add(Import.class.getName());
    candidateIndicators.add(ImportResource.class.getName());
}

public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
    // Do not consider an interface or an annotation...
    if (metadata.isInterface()) {
        return false;
    }

    // Any of the typical annotations found?
    for (String indicator : candidateIndicators) {
        if (metadata.isAnnotated(indicator)) {
            return true;
        }
    }

    // Finally, let's look for @Bean methods...
    try {
        return metadata.hasAnnotatedMethods(Bean.class.getName());
    } catch (Throwable ex) {
        if (logger.isDebugEnabled()) {
            logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
        }
        return false;
    }
}

Determine what is stored in the configCandidates variable

Configure whether the class is empty. If not, sort it.

Create the ConfigurationClassParser object, which is used to parse @ Configuration class, complete package scanning and BeanDefinition registration. This is mainly done by executing the parser.parse(candidates); method.

Before executing the parser.parse(candidates) method:

After executing the parser.parse(candidates) method:

After parsing the configuration class, this. Reader. Loadbean definitions (configclasses); method is executed. This method is mainly used to handle the Import class, @ Bean and @ ImportResource annotations. We will talk about the details of these two methods next time.

Finally, we add the beans needed to support the ImportAware interface.

// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
    sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}

We'll talk about the use of the ImportAware interface next time.

To be continued...

Source learning notes: https://github.com/shenjing/spring-code-study

Welcome to pay attention to the public account and learn and grow together.

Posted by Mr. Tech on Tue, 15 Oct 2019 23:48:06 -0700