General documentation: Article directory Github: github.com/black-ant

A. The preface

By accident, I have completed the source code related to auto assembly, so I can finally open the series.

In this series, we will take a look at the process of automatic assembly. We started to learn the whole process a year ago, but have not found the appropriate expression form. Recently, inspiration came to us, so we can try this method.

2. Background knowledge

2.1 Basic Background

Spring Boot autoconfiguration attempts to auto-configure Spring applications based on the ADDED JAR dependencies. Spring extends the flexibility of Spring by allowing autoassembly to be replaced and self-configured.

The spring-boot-Autoconfiture package contains several configuration files:

  • Spring.factories: the auto-assembly classes that are used when Spring starts, which we’ll examine later
  • Spring-autoconfigure-metadata. properties: indicates the configuration information
  • Spring-configure-metadata. json: configure-metadata.json: configure-metadata.json: configure-metadata. this file is used by services to map and process properties
    • For example, spring.jpa.Hibernate. Dddl-auto provides multiple schemas

{
        "name": "spring.jpa.hibernate.ddl-auto"."values": [{"value": "none"."description": "Disable DDL handling."
            },
            {
                "value": "validate"."description": "Validate the schema, make no changes to the database."
            },
            {
                "value": "update"."description": "Update the schema if necessary."
            },
            {
                "value": "create"."description": "Create the schema and destroy previous data."
            },
            {
                "value": "create-drop"."description": "Create and then destroy the schema at the end of the session."}}]Copy the code

Automatic assembly and automatic configuration

  • Automatic configuration: Provided by Spring Boot to automatically configure applications by relying on JAR packages.
    • For example, when we introduced spring-boot-starter- Web, we automatically introduced the Spring MVC related JAR package to automatically configure Spring MVC.
  • Autowire: This is the IoC injection method provided by Spring. See the Spring Tutorial — Beans Autowire documentation for details.

2.2 Basic Usage

Let’s take a look at the basics:

@Configuration
@EnableAutoConfiguration
public class BeanAnnotationConfig {

    @Bean
    public BeanAnnotationTestService getBeanAnnotationTestService(a) {
        return newBeanAnnotationTestService(); }}Copy the code

Implementation basics: Autoassemble is implemented based on the @SpringBootApplication annotation

F- SpringBootApplication
    |- @SpringBootConfiguration: is this a Spring tags | - the Boot configuration class@EnableAutoConfiguration: used to unlock the function of automatic configuration | -@AutoConfigurationPackagePath: access to the main program of package, and will package path (including a package) of all components to register | - Spring IOC container@Import: (and by AutoConfigurationImportSelector) | - import resources@ComponentScan(internal for excludeFilters)

Copy the code

Three. Source analysis

To try a new way to parse source code:

3.1 Starting point of the scan

3.2 Service Scan Relationship

Let’s start with the basic logic:

1. Start from ConfigurationClassPostProcessor 2. In ConfigurationClassPostProcessor doProcessConfigurationClass all config class information, including @ Bean Bean 3 annotations. Finally in ConfigurationClassBeanDefinitionReader loadBeanDefinitionsForBeanMethod is loaded

C01 – ConfigurationClassPostProcessor: handle processConfigBeanDefinitions types

SequenceDiagram M102->>M102 ->>M102 ->>M102 doWhile: do-while parse loop M102 doWhile->>M102 doWhile: ConfigurationClassBeanDefinitionReader processing M102 doWhile - > > M102 doWhile: judging handled and generate new untreated M102 doWhile - > > M102: M102->>M102: registers ImportRegistry as a bean M102->>M102: clears the cache in the externally provided MetadataReaderFactory
C01 - ConfigurationClassPostProcessor M101 postProcessBeanDefinitionRegistry - System. IdentityHashCode (registry) to get one RegistryId - the id to join the Set < Integer > - processConfigBeanDefinitions (registry) : Detailed see M102 M102 processConfigBeanDefinitions - get a List from BeanDefinitionRegistry < BeanDefinitionHolder > collection? Is the Attribute of each BeanDefinition a configurationClass? - here is to add BeanDefinitionRegistry for all classes of filtered - a sorting through the Order - get related Generator, and get a through the Generator Definitionholder < definitionHolder > configurationClassparser-list; Ensure its uniqueness FOR - cycle all BeanDefinitionHolder - by dealing with the collection - through ConfigurationClassBeanDefinitionReader ConfigurationClassParser load configClasses// M01 pseudo-code
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    int registryId = System.identityHashCode(registry);
    / /...
    this.registriesPostProcessed.add(registryId);
    processConfigBeanDefinitions(registry);
}

// M02 pseudocode
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    
    // Step 1: Obtain the configurationClass
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();
    / /... Relevant judgments are made here
    configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
		
    // Step 2: sort
    configCandidates.sort((bd1, bd2) -> {
        int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
        int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
        return Integer.compare(i1, i2);
    });

    // Omit: Generate the necessary arguments for ConfigurationClassParser
    
    // Step 3: prepare a ConfigurationClassParser
    ConfigurationClassParser parser = new ConfigurationClassParser(
        this.metadataReaderFactory, this.problemReporter, this.environment,
        this.resourceLoader, this.componentScanBeanNameGenerator, registry);

    // Set BeanDefinitionHolder for ConfigClass
    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
    do {
        // Step 4: Parse processing
        parser.parse(candidates);
        parser.validate();

        // Prepare the ConfigurationClass and remove the one already processed
        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);

        // Step 5: Process the reader
        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(
                registry, this.sourceExtractor, this.resourceLoader, this.environment,
                this.importBeanNameGenerator, parser.getImportRegistry());
        }
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);

    }while(! candidates.isEmpty());// Register ImportRegistry as a bean to support the importtaware@Configuration class
    if(sbr ! =null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
        sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
    }

    if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
        // Clear the cache in the externally supplied MetadataReaderFactory
        ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); }}Copy the code

C02- ConfigurationClassParser: Primary processing logic

ConfigurationClassParser is used to resolve a single configuration class. However, because of the @import annotation, there may be multiple Configurationclasses


SequenceDiagram M202->>M202: FOR loop processing SourceClass M202->>M203 Call processConfigurationClass M203 grenade launcher - > > M204: call doProcessConfigurationClass M204 - > > M204: Circulation processing PropertySources M204 - > > M204: circulation processing ComponentScans M204 - > > M204: recursive processing @ Bean method M204 - > > M204: @Import M204->>M204: get superclass M204->>M203: return superclass or null M203->>M203: recursively handle superclass M203->>M203: add processing class to Map set
C02 -configurationClassparser M201 -parse (Set< definitionHolder > configCandidates) -set < definitionholder > C02 -configurationClassparser M201 -parse (Set< definitionHolder > configCandidates) -set < definitionHolder >, Different parse - is called depending on its typethis.deferredImportSelectorHandler.process(); ? - call inner class DeferredImportSelectorHandler M202 - processImports - processing related logicthis.importStack.push(configClass) : 
        // After this point, the core Class processing logic will determine the type of SourceClass separately
        FOR- Collection<SourceClass> : forCirculation of the incoming set (Collection - Group. Entry. GetImportClassName), obtain SourceClass1- TODO
            2- TODO
            3- Apart from the above2Kind of outside processConfigurationClass (candidate. AsConfigClass (configClass), exclusionFilter) : M203 M203- processConfigurationClass - asSourceClass(configClass, filter) : Get SourceClass - SourceClass = doProcessConfigurationClass (configClass, SourceClass, filter) : M204 - TODO for M204 doProcessConfigurationClass - if the class notes is Component, called processMemberClasses (configClass, SourceClass, filter) - For loop on the PropertySources annotation, And call processPropertySource(propertySource) -for to loop on which ComponentScans annotations -this. ComponentScanParser. Parse get Set < BeanDefinitionHolder > the FOR - Set < BeanDefinitionHolder >1- get BeanDefinition2- Call parse(bdcand.getBeanClassName (), Holder-.getBeanname ()) if the given bean definition is checked as a candidate for configuration class? - This can be viewed as a recursive processing - call processImports processing@ImportAnnotate the corresponding class? - this is a recursive process - ImportResource corresponding attributes the IF - IF the corresponding attribute is not null - ImportResource. GetStringArray ("locations") : Locations of these attributes String [] - get BeanDefinitionReader FOR - circular array - configClass. AddImportedResource (resolvedResource, ReaderClass) - retrieveBeanMethodMetadata (sourceClass) : access to them@BeanAbove the corresponding Method FOR - cycle Set < MethodMetadata > - configClass. AddBeanMethod (newBeanMethod(methodMetadata, configClass)); - processInterfaces(configClass, sourceClass) : processes processesinterface defaultMethod - Gets the parent type and adds it toMap<String.ConfigurationClass> -return sourceClass.getSuperClass() : find the superclass, return its annotation metadata and recurseM05- processMemberClasses

Copy the code

C03- DeferredImportSelectorHandler

C03- DeferredImportSelectorHandler M301- process() : - will List < DeferredImportSelectorHolder > in the sorted into DeferredImportSelectorGroupingHandler - call handler. ProcessGroupImports () Process all groupsCopy the code

C04- DeferredImportSelectorGroupingHandler

SequenceDiagram M401->>M401 ->>M401 ->>M401 ->>M401 Obtain Group in the total process processing logic DeferredImportSelectorHolder M401 - > > M501: performing Group, total process processing logic
C04 - DeferredImportSelectorGroupingHandler M401 DeferredImportSelectorGrouping - - processGroupImports - first cycle grouping.getImports() : Returns the Iterable < Group. Entry > - obtain Group DeferredImportSelectorHolder - DeferredImportSelector. Each Group processing (process) DeferredImportSelectorHolder (C05 logic) FOR each DeferredImportSelectorGrouping Imports - - second cyclethis.configurationClasses.get(entry.getMetadata()) : ConfigurationClass - processImports Method logic (M202)Copy the code

C05- AutoConfigurationImportSelector

From the previous step, one of which is DeferredImportSelectorHolder AutoConfigurationImportSelector

SequenceDiagram M401->>M501 ->>M502: AutoConfigurationEntry M502->>M502 Get all configurations M502->>M502: filter repetition and filter to be skipped ConfigClass M502->>M503: Process listener and event M502->>M501: M501->>M401: AutoConfigurationEntry adds Map<String, AnnotationMetadata>
C05- AutoConfigurationImportSelector M502- getAutoConfigurationEntry - getAttributes(annotationMetadata) : Get AnnotationMetadata properties - getCandidateConfigurations (AnnotationMetadata, attributes) : Get all configurations lists - removeDuplicates: removes duplicate configuration classes - checkExcludedClasses + removeAll: Remove getExclusions (annotationMetadata, Class - getConfigurationClassFilter attributes) (.) filter (configurations) - filter. The match (candidates,thisAutoConfigurationMetadata) : whether the filter to filter skip - fireAutoConfigurationImportEvents (configurations, exclusions) : See fireAutoConfigurationImportEvents -newAutoConfigurationEntry(configurations, exclusions) : Returns a AutoConfigurationEntry M503 fireAutoConfigurationImportEvents - getAutoConfigurationImportListeners () : Get AutoConfigurationImportListener collection - the listener. OnAutoConfigurationImportEvent (event) : through each listern respectively, C05PSC01- AutoConfigurationGroup? Static class - this class for internal M501 - process: - AutoConfigurationImportSelector. GetAutoConfigurationEntry (annotationMetadata) : Get an AutoConfigurationEntry object? - see getAutoConfigurationEntry methodthis.autoConfigurationEntries.add(autoConfigurationEntry) : Add to the AutoConfigurationEntry set for-list <AutoConfigurationEntry> : FOR loops through the entire set -thisEntries. PutIfAbsent (importClassName, annotationMetadata) : Insert Map<String, annotationMetadata >? - This means that all autoConfigurationEntries are reprocessed into maps// M501 pseudo-code
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {                                      
    AutoConfigurationImportSelector.class.getSimpleName(),
    deferredImportSelector.getClass().getName()));
    // Call M502 to generate an AutoConfigurationEntry
    AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    Map
      ,>
    for (String importClassName : autoConfigurationEntry.getConfigurations()) {
        this.entries.putIfAbsent(importClassName, annotationMetadata); }}// M502 core pseudocode
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if(! isEnabled(annotationMetadata)) {return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    
    // Remove and filter
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = getConfigurationClassFilter().filter(configurations);
    
    // Call the Listener
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}



                
Copy the code

C06- ConfigurationClass

In c02-M204, we can see that the annotations on the class and the beans in the class are configured to a ConfigurationClass

C06- ConfigurationClass - getBeanMethods() : Observation and found its place is called ConfigurationClassBeanDefinitionReader# loadBeanDefinitionsForConfigurationClassCopy the code

C07- ConfigurationClassBeanDefinitionReader

M701->> diagram M701->> diagram M701->> diagram M701->> diagram M701: If it is Imported, call 702 registered M701 - > > M703: For loop loadBeanDefinitionsForBeanMethod M703 - > > M703: Judgments @ Bean @ Lazy, annotation M703 - > > M703: processing methods such as autowire M703 - > > M703: enclosing registry. RegisterBeanDefinition registered
C07 ConfigurationClassBeanDefinitionReader M - loadBeanDefinitions M701 loadBeanDefinitionsForConfigurationClass - judge the current Whether to skip configClasstrue: removes - from related objectsthis.registry.removeBeanDefinition(beanName)
                - this. ImportRegistry. RemoveImportingClass (configClass. For getMetadata (). The getClassName ()) - if they are Imported, Call registerBeanDefinitionForImportedConfigurationClass (M702) FOR - circulation processing configClass. GetBeanMethods Bean method - () loadBeanDefinitionsForBeanMethod(beanMethod) :M703 M702- registerBeanDefinitionForImportedConfigurationClass - configClass.getMetadata() : Get AnnotationMetadata - by AnnotationMetadata AnnotatedGenericBeanDefinition - generated by AnnotatedGenericBeanDefinition generated ScopeMetadata , And set the Scope for AnnotatedGenericBeanDefinition - by BeanNameGenerator generate a Config BeanName - create a BeanDefinitionHolder, andthis. Registry. - configClass registerBeanDefinition registration. SetBeanName (configBeanName) : For a Class named M703 - loadBeanDefinitionsForBeanMethod (beanMethod) : To load Bean main logic, here has method to deal with - beanMethod. GetConfigurationClass () : get the current ConfigurationClass - beanMethod. For getMetadata () : Get MethodMetadata, this metadata contains information about current methods - if the current method should skip, is added to the configClass. SkippedBeanMethods, And return - AnnotationConfigUtils. AttributesFor (metadata, Bean class) : get the current methods on the Bean annotation attributes - Bean. GetStringArray ("name") Get a collection of names, taking the first as the Bean name and the others as aliases -???? - through configClass and metadata to build a ConfigurationClassBeanDefinition - for extractSource ConfigurationClassBeanDefinition Settings ? - configClass.getResource() IF- Determines whether it is an annotation@BeanStatic method oftrue:
                false: set FactoryBeanName and UniqueFactoryMethodName - beanDef. SetFactoryBeanName (configClass. GetBeanName ()) : Config Settings on the bean's name - beanDef. SetUniqueFactoryMethodName (methodName) : Name of the current method -beandef. setAutowireMode: Sets the mode of injection, in this case AUTOWIRE_CONSTRUCTOR? - AUTOWIRE_NO / AUTOWIRE_BY_NAME / AUTOWIRE_BY_TYPE / AUTOWIRE_CONSTRUCTOR / AUTOWIRE_AUTODETECT - beanDef.setAttribute : Sets skipRequiredCheck totrue- AnnotationConfigUtils. ProcessCommonDefinitionAnnotations: setting load type? - Includes Lazy, Primary, DependsOn, Role, description-bean. GetEnum ("autowire")  + beanDef.setAutowireMode(autowire.value())
        - bean.getBoolean("autowireCandidate") + setAutowireCandidate
        - bean.getString("initMethod") +  beanDef.setInitMethodName(initMethodName)
        - bean.getString("destroyMethod") + beanDef.setDestroyMethodName(destroyMethodName) - AnnotationConfigUtils.attributesFor(metadata, Scope.class) : Get + beandef.setScope to configure Scope attributes - attributes.getenum ()"proxyMode") : obtain proxyMode - ScopedProxyCreator. CreateScopedProxy: By getting proxyMode + registry + beanDef construct BeanDefinitionHolder - such as through BeanDefinitionHolder + configClass + metadata build ConfigurationClassBeanDefinition -this. Registry. RegisterBeanDefinition (beanName beanDefToRegister) : the last registered Bean? -Registry is a BeanDefinitionRegistryCopy the code

conclusion

This is a test document. I want to test how the source code process should be presented, and I don’t know if this document is effective.

I will continue to deal with the whole article and try to find a more appropriate pattern.

Automatic assembly belongs to the earlier articles in the notes, and there are many local IOC and Cloud articles. It should be well presented after finding the appropriate mode!!