Spring IOC: ComponentScanBeanDefinitionParser call chain

Posted by imnsi on Wed, 26 Jan 2022 16:18:48 +0100

reference material:

Learning Spring IoC source code: Context: detailed explanation of component scan node

Previously:
 Spring IOC: parseCustomElement call chain

Written at the beginning: This article is a personal study note. The content is relatively casual and mixed with personal understanding. If there are mistakes, you are welcome to correct them.

catalogue

1, parse

2, configureScanner

        1,configureScanner

        2,createScanner

        3,registerDefaultFilters

         4,parseTypeFilters

        5,createTypeFilter

3, doScan

        1,doScan

        2, findCandidateComponents

        2.1,isCandidateComponent

         2.2,isCandidateComponent

        3,resolveScopeMetadata

        4,postProcessBeanDefinition

        5,processCommonDefinitionAnnotations

        6,checkCandidate

        7,applyScopedProxyMode

        8,registerBeanDefinition

3, registerComponents

        1,registerComponents

        2,registerAnnotationConfigProcessors

Summary:

1, parse

In the previous article, we learned that when the element is a < context: component scan / > node, findParserForElement(element, parserContext) will return ComponentScanBeanDefinitionParser, and then execute the parse method of ComponentScanBeanDefinitionParser class.

@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
    // 1.findParserForElement: find the corresponding BeanDefinition parser for the element
    // 2. Use the BeanDefinition parser to parse the element node
    return findParserForElement(element, parserContext).parse(element, parserContext);
}

In the parse method, we first parse the base package attribute, get the path to be scanned and parse it. If placeholders are used, we need to replace them. If separators "," or ";" are used , it also needs to be split into arrays. Then we call the configureScanner method to build ClassPathBeanDefinitionScanner to prepare for subsequent scanning. After getting the ClassPathBeanDefinitionScanner, call the doScan method to add the class of related annotations to the beanDefinition under the scanning path. Finally, register other components, such as post processor, etc.

@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
    // Get the base package attribute value of the < context: component scan > node
    String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
    // Resolve placeholders, such as ${basePackage}
    basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
    // Parse base package (multiple are allowed to be filled in through any symbol in ",; \ t\n")
    // String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
    String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
            ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
 
    // Actually scan for bean definitions and register them.
    // Build and configure ClassPathBeanDefinitionScanner
    ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    // Use the scanner to scan in the specified basePackages package and return the registered bean definition
    Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
    // Component registration (including registering some internal annotation post processors and triggering registration events)
    registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
 
    return null;
}

2, configureScanner

        1,configureScanner

After entering the configureScanner method, you will first read whether the use default filters attribute is configured under the node. This attribute is used to specify whether to use the default filter (note that it is a filter, not a filter rule). Then the createScanner method is called to create the scanner. After creation, some other attributes, such as resource-pattern, name-generator, are parsed. Finally, call the parseTypeFilters method to parse the type filter.

protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
    // Resolve the use default filters attribute, which is true by default and indicates whether to use the default filter
    boolean useDefaultFilters = true;
    if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
        useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
    }
 
    // Delegate bean definition registration to scanner class.
    // Build ClassPathBeanDefinitionScanner and delegate bean definition registration to scanner class
    ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
    scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
    scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
 
    // Resolve the resource pattern attribute
    if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
        scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
    }
 
    try {
        // Resolve name generator attribute
        parseBeanNameGenerator(element, scanner);
    }
    catch (Exception ex) {
        parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
    }
 
    try {
        // Resolve scope resolver and scoped proxy attributes
        parseScope(element, scanner);
    }
    catch (Exception ex) {
        parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
    }
 
    // Resolve type filter
    parseTypeFilters(element, scanner, parserContext);
 
    return scanner;
}

        2,createScanner

The createScanner method determines whether to use the default filter. If so, continue to call the registerDefaultFilters method

protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {
    return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters,
            readerContext.getEnvironment(), readerContext.getResourceLoader());
}
 
// ClassPathBeanDefinitionScanner.java
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
        Environment environment, ResourceLoader resourceLoader) {
 
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    this.registry = registry;
 
    if (useDefaultFilters) {
        // Register the default filter
        registerDefaultFilters();
    }
    setEnvironment(environment);
    setResourceLoader(resourceLoader);
}
 

        

        3,registerDefaultFilters

The registerDefaultFilters method will add the filter corresponding to @ Component and @ ManagedBean to includeFilters.

// ClassPathScanningCandidateComponentProvider.java
protected void registerDefaultFilters() {
    // Add @ Component annotation to includeFilters
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
    try {
        // Add @ ManagedBean annotation to includeFilters
        this.includeFilters.add(new AnnotationTypeFilter(
                ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
        logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
        // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
    }
    try {
        // Add @ Named annotation to includeFilters
        this.includeFilters.add(new AnnotationTypeFilter(
                ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
        logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
        // JSR-330 API not available - simply skip.
    }
}

         4,parseTypeFilters

Resolve the child nodes under the current node, read the possible include filter and exclude filter nodes, and then call the createTypeFilter method for the read attributes to create a typeFilter and add it to the scanner.

protected void parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext) {
    // Parse exclude and include filter elements.
    ClassLoader classLoader = scanner.getResourceLoader().getClassLoader();
    NodeList nodeList = element.getChildNodes();
    // Traverse and resolve all child nodes under element
    for (int i = 0; i < nodeList.getLength(); i++) {
        Node node = nodeList.item(i);
        if (node.getNodeType() == Node.ELEMENT_NODE) {
            // Get the localName of the node
            // For example, node: < context: exclude filter type = "" expression = "" / >, localName: Exclude Filter
            String localName = parserContext.getDelegate().getLocalName(node);
            try {
                /**
                 * for example
                 * <context:component-scan base-package="com.joonwhee.open">
                 *     <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
                 * </context:component-scan>
                 */
                // Resolve include filter child nodes
                if (INCLUDE_FILTER_ELEMENT.equals(localName)) {
                    // Build TypeFilter
                    TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
                    // Added to the includeFilters property of the scanner
                    scanner.addIncludeFilter(typeFilter);
                }
                // Resolve exclude filter child nodes
                else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) {
                    // Build TypeFilter
                    TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
                    // The excludeFilters property added to the scanner
                    scanner.addExcludeFilter(typeFilter);
                }
            } catch (Exception ex) {
                parserContext.getReaderContext().error(
                        ex.getMessage(), parserContext.extractSource(element), ex.getCause());
            }
        }
    }
}
 

        5,createTypeFilter

Read the type and expression attributes in the include filter and exclude filter nodes, and create an AnnotationTypeFilter according to different attributes. For example, when type="annotation", it indicates that the annotation is filtered by annotation. If the annotation path is set in expression, such as expression="org.springframework.stereotype.Controller", the @ controller annotation will be filtered out.

protected TypeFilter createTypeFilter(Element element, ClassLoader classLoader, ParserContext parserContext) {
    // Get type and expression
    String filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE);
    String expression = element.getAttribute(FILTER_EXPRESSION_ATTRIBUTE);
    expression = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(expression);
    try {
        // Return the corresponding TypeFilter according to the filterType. For example, annotation returns annotation TypeFilter
        if ("annotation".equals(filterType)) {
            // Specify the filtered annotation. expression is the full name of the annotation class, for example: org springframework. stereotype. Controller
            return new AnnotationTypeFilter((Class<Annotation>) classLoader.loadClass(expression));
        }
        else if ("assignable".equals(filterType)) {
            // Specify the filtered class or interface, including subclasses and sub interfaces. expression is the full name of the class
            return new AssignableTypeFilter(classLoader.loadClass(expression));
        }
        else if ("aspectj".equals(filterType)) {
            // Specify aspectj expression to filter the class, and expression is the aspectj expression string
            return new AspectJTypeFilter(expression, classLoader);
        }
        else if ("regex".equals(filterType)) {
            // Filter classes through regular expressions. Expression is a regular expression string
            return new RegexPatternTypeFilter(Pattern.compile(expression));
        }
        else if ("custom".equals(filterType)) {
            // User defined filter type. expression is the full name of the user-defined filter class
            Class<?> filterClass = classLoader.loadClass(expression);
            // The customized filter must implement the TypeFilter interface, otherwise an exception will be thrown
            if (!TypeFilter.class.isAssignableFrom(filterClass)) {
                throw new IllegalArgumentException(
                        "Class is not assignable to [" + TypeFilter.class.getName() + "]: " + expression);
            }
            return (TypeFilter) BeanUtils.instantiateClass(filterClass);
        }
        else {
            throw new IllegalArgumentException("Unsupported filter type: " + filterType);
        }
    }
    catch (ClassNotFoundException ex) {
        throw new FatalBeanException("Type filter class not found: " + expression, ex);
    }
}

3, doScan

        1,doScan

The doScan method will call findCandidateComponents to find all beans that meet the requirements (that is, beans that use annotations and meet the filter filter rules). Traversing all the required bean, parsing @Scope, generating beanName, and then calling postProcessBeanDefinition and processCommonDefinitionAnnotations to further analyze candidate bean. Subsequently, encapsulate the BeanDefinitionHolder, then select whether to create a scope proxy according to the value of proxyMode, and finally register BeanDefinition (i.e. beanDefinitionMap, beanDefinitionNames and aliasMap caches)

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
    // Traverse basePackages
    for (String basePackage : basePackages) {
        // Scan the basePackage and find all the bean definitions that meet the requirements (the most common one that meets the requirements here is to use the Component annotation)
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        // Traverse all candidate bean definitions
        for (BeanDefinition candidate : candidates) {
            // Resolve @ Scope annotations, including scopename (singleton by default, and prototype by common), and proxymode (proxy is not used by default, and interface proxy / class proxy is optional)
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            // Use the beanName generator to generate beanName
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            if (candidate instanceof AbstractBeanDefinition) {
                // Further handle the BeanDefinition object, such as whether this bean can be automatically assembled into other beans
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            if (candidate instanceof AnnotatedBeanDefinition) {
                // Handle the general annotations defined on the target class, including @ Lazy, @Primary, @DependsOn, @Role, @Description
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            // Check whether beanName has been registered. If so, check whether it is compatible
            if (checkCandidate(beanName, candidate)) {
                // Encapsulate the bean definition and beanName of the current traversal bean into BeanDefinitionHolder
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                // Select whether to create a scope proxy according to the value of proxyMode (resolved in step 4)
                definitionHolder =
                        AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                // Register BeanDefinition (register to beanDefinitionMap, beanDefinitionNames, aliasMap cache)
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

 

        2, findCandidateComponents

Read the configured path, load all classes, and use the filter to check whether the given class is a candidate class. If it meets the requirements, it will be added to candidates

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
    try {
        // Assemble the generic package path to be scanned according to the package name we configured, for example: com joonwhee. Open will be assembled as: classpath *: COM / joonwhee / open / * * / * class
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        // Get all the matching class resources according to the path matching of the generic configuration package (the jar s that this project depends on will be scanned together if the paths also match)
        Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
        boolean traceEnabled = logger.isTraceEnabled();
        boolean debugEnabled = logger.isDebugEnabled();
        // Traverse all matching class resources
        for (Resource resource : resources) {
            if (traceEnabled) {
                logger.trace("Scanning " + resource);
            }
            if (resource.isReadable()) {
                try {
                    // Use metadatareader to read resources. Metadatareader is a class specially used to access metadata (including class metadata, annotation metadata, annotation metadata, etc.)
                    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                    // Use the Filter to check whether the given class is a candidate class (candidate class: does not match all filters of excludeFilters and matches at least one Filter of includeFilters)
                    if (isCandidateComponent(metadataReader)) {
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setResource(resource);
                        sbd.setSource(resource);
                        // Judge whether sbd is a candidate class (independent & & (specific implementation class | (abstract class & & class has methods using @ Lookup annotation))
                        if (isCandidateComponent(sbd)) {
                            if (debugEnabled) {
                                logger.debug("Identified candidate component class: " + resource);
                            }
                            // If it is determined that it is a candidate class, it is added to candidates
                            candidates.add(sbd);
                        }
                        else {
                            if (debugEnabled) {
                                logger.debug("Ignored because not a concrete top-level class: " + resource);
                            }
                        }
                    }
                    else {
                        if (traceEnabled) {
                            logger.trace("Ignored because not matching any filter: " + resource);
                        }
                    }
                }
                catch (Throwable ex) {
                    throw new BeanDefinitionStoreException(
                            "Failed to read candidate component class: " + resource, ex);
                }
            }
            else {
                if (traceEnabled) {
                    logger.trace("Ignored because not readable: " + resource);
                }
            }
        }
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
    }
    return candidates;
}

        2.1,isCandidateComponent

Judge whether the metadata reader (which is a class specially used to access metadata) matches the TypeFilter in excludeFilters and includeFilters.

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    for (TypeFilter tf : this.excludeFilters) {
        if (tf.match(metadataReader, this.metadataReaderFactory)) {
            // If the metadata reader matches any one of the excludeFilters, false is returned, indicating that the class corresponding to the metadata reader is not a candidate class
            return false;
        }
    }
 
    // includeFilters includes: org.org by default springframework. stereotype. Component annotation, javax annotation. Managedbean annotation
    for (TypeFilter tf : this.includeFilters) {
        if (tf.match(metadataReader, this.metadataReaderFactory)) {
            // If the metadataReader matches any TypeFilter in includeFilters (if tf is Component annotation: the class corresponding to the metadataReader uses component, it matches),
            // Then judge whether the @ Conditional annotation matches (@ Conditional is basically not used, and it will not be deeply analyzed here); If it matches, it returns true, indicating that the class corresponding to the metadata reader is a candidate class
            return isConditionMatch(metadataReader);
        }
    }
    return false;
}

         2.2,isCandidateComponent

Continue to judge whether the class meets the requirements.

protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    AnnotationMetadata metadata = beanDefinition.getMetadata();
    // isIndependent: determines whether the underlying class is independent, that is, whether it is a top-level class or nested class (static inner class). It can be constructed independently of the closed class.
    // isConcrete: the returned underlying class is a concrete class, that is, it is neither an interface nor an abstract class.
    // isAbstract: Returns whether the underlying class is marked as abstract.
    // hasAnnotatedMethods: determines whether the base class has any methods to annotate (or meta annotate) with the given annotation (@ Lookup) type.
    return (metadata.isIndependent() && (metadata.isConcrete() ||
            (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}

        3,resolveScopeMetadata

Parse the @ Scope annotation to determine whether to use the proxy mode. defaultProxyMode depends on the Scope resolver and scoped proxy properties read by the parseScope method in the previous article. It can be set through scoped proxy , and the default is scopedproxymode NO.

@Override
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
    ScopeMetadata metadata = new ScopeMetadata();
    if (definition instanceof AnnotatedBeanDefinition) {
        AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
        AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
                annDef.getMetadata(), this.scopeAnnotationType);
        // If @ Scope annotation is used
        if (attributes != null) {
            // Resolve scopeName property
            metadata.setScopeName(attributes.getString("value"));
            // Resolve proxyMode property
            ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
            if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) {
                proxyMode = this.defaultProxyMode;
            }
            metadata.setScopedProxyMode(proxyMode);
        }
    }
    return metadata;
}

 

        4,postProcessBeanDefinition

This step sets the default value for beanDefinition and reads autowire candidate. This attribute is used to set whether a bean is a candidate bean during automatic injection.

protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
    // Set the default value for beanDefinition
    beanDefinition.applyDefaults(this.beanDefinitionDefaults);
    if (this.autowireCandidatePatterns != null) {
        // Set whether this bean can be automatically assembled into other beans. The default value is true
        beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
    }
}
<bean id="beantest" class="com.test" autowire-candidate="false"/>

        5,processCommonDefinitionAnnotations

Continue to resolve common attributes, including @ Lazy, @Primary, @DependsOn, @Role, @Description.

public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
    processCommonDefinitionAnnotations(abd, abd.getMetadata());
}
 
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
    // Parse @ Lazy annotation and set whether to delay loading
    if (metadata.isAnnotated(Lazy.class.getName())) {
        abd.setLazyInit(attributesFor(metadata, Lazy.class).getBoolean("value"));
    }
    else if (abd.getMetadata() != metadata && abd.getMetadata().isAnnotated(Lazy.class.getName())) {
        abd.setLazyInit(attributesFor(abd.getMetadata(), Lazy.class).getBoolean("value"));
    }
 
    // Parse the @ Primary annotation. When multiple beans match during automatic assembly, the Bean annotated as @ Primary will be the first choice, otherwise an exception will be thrown
    // (the scene is small. If there may be multiple matches, you can use the combination of @ Autowired @Qualifier)
    if (metadata.isAnnotated(Primary.class.getName())) {
        abd.setPrimary(true);
    }
    // Parse @ DependOn annotation
    if (metadata.isAnnotated(DependsOn.class.getName())) {
        abd.setDependsOn(attributesFor(metadata, DependsOn.class).getStringArray("value"));
    }
 
    if (abd instanceof AbstractBeanDefinition) {
        // Parse @ Role annotation
        AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd;
        if (metadata.isAnnotated(Role.class.getName())) {
            absBd.setRole(attributesFor(metadata, Role.class).getNumber("value").intValue());
        }
        // Parse @ Description annotation
        if (metadata.isAnnotated(Description.class.getName())) {
            absBd.setDescription(attributesFor(metadata, Description.class).getString("value"));
        }
    }
}

        6,checkCandidate

Use beanDefinitionMap to judge whether the beanName is already in the cache. If it is not, return it directly. If it is, take out the BeanDefinition of the beanName in beanDefinitionMap and continue to judge. If an agent is used, take out the BeanDefinition of the agent and compare it with the BeanDefinition to be registered as the value in the cache to judge whether it is compatible.

protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
    // If the registry (beanDefinitionMap cache) does not contain beanName, it returns true, indicating that the bean definition can be registered
    if (!this.registry.containsBeanDefinition(beanName)) {
        return true;
    }
    // If the registry contains beanName
    // Get the BeanDefinition of the beanName in the registry
    BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
    // Get the original BeanDefinition (the BeanDefinition using proxy will have the original BeanDefinition)
    BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
    if (originatingDef != null) {
        // If there is an original BeanDefinition, the original BeanDefinition is used
        existingDef = originatingDef;
    }
    // Check whether the new BeanDefinition is compatible with the original BeanDefinition. If it is compatible, return false and skip registration
    if (isCompatible(beanDefinition, existingDef)) {
        return false;
    }
    // If incompatible, throw an exception
    throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +
            "' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
            "non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
}

        7,applyScopedProxyMode

Judge whether to use proxy mode. If so, continue to judge whether to use interface based or class based proxy. The former uses CGlib proxy and the latter uses JDK dynamic proxy. After confirming the proxy mode, create the corresponding scope proxy.

static BeanDefinitionHolder applyScopedProxyMode(
        ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
 
    ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
    // If you do not need to create a proxy, you can return the bean definition directly
    if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
        return definition;
    }
    // Judge whether to use class based proxy or interface based code. Class based: CGLIB proxy, interface based: JDK dynamic proxy
    boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
    // Create a scope proxy using the corresponding proxy mode
    return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
}

 

        8,registerBeanDefinition

Call the BeanDefinitionReaderUtils tool class to complete the registration of BeanDefinition. This method has been described in the default namespace node resolution( Spring IOC: parseDefaultElement call chain Section 5), which will not be repeated here. It is mainly used to register beanDefinitionMap, beanDefinitionNames and aliasMap caches.

protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
    // Call the BeanDefinitionReaderUtils tool class to complete the registration of BeanDefinition
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}

3, registerComponents

        1,registerComponents

To determine whether a registered annotation based bean postprocessor is required, the default is treu, then the registerAnnotationConfigProcessors method is used to register.

protected void registerComponents(
        XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {
 
    Object source = readerContext.extractSource(element);
    // Use annotated tagName (e.g. context: component scan) and source to build componentdefinition
    CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);
 
    // Add all the scanned beandefinitions to the nestedComponents property of compositeDef
    for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {
        compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));
    }
 
    boolean annotationConfig = true;
    if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
        // Get the annotation config attribute value of the component scan tag (the default is true)
        annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
    }
    if (annotationConfig) {
        // If the annotation config attribute value is true, register all Bean post processors for annotation in the given registry
        Set<BeanDefinitionHolder> processorDefinitions =
                AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
        for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
            // Add the BeanDefinition of the registered annotation post processor to the nestedComponents property of compositeDef
            compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
        }
    }
 
    // Trigger the component registration event. The default implementation is EmptyReaderEventListener (empty implementation, no specific operation)
    readerContext.fireComponentRegistered(compositeDef);
}

 

        2,registerAnnotationConfigProcessors

To determine whether the commonly used annotation BeanDefinition includes @Configuration, @Autowired, @Required, @Resource and other post based processor annotations, and then call registerPostProcessor registration.

The core registration method registerBeanDefinition is Spring IOC: parseDefaultElement call chain Section 5 has been analyzed and omitted here.

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
        BeanDefinitionRegistry registry, Object source) {
 
    DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
    if (beanFactory != null) {
        if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
            // Set the dependencyComparator property
            beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
        }
        if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
            // Set the autowirecandideresolver property (set the parser that automatically injects candidate objects to determine whether the BeanDefinition is a candidate object)
            beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        }
    }
 
    Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);
 
    // Register the internally managed bean of the post processor used to process the @ Configuration annotation
    if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
        def.setSource(source);
        // registerPostProcessor: register BeanDefinition in the registry
        beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
 
    // Register internally managed bean s for post processor processing @ Autowired, @ Value, @ Inject and @ Lookup annotations
    if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
 
    // Register internally managed bean s for post processor processing @ Required annotation
    if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
 
    // Register internally managed bean s for post processor processing JSR-250 annotations (e.g. @ Resource, @PostConstruct, @PreDestroy)
    // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
    if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
 
    // Register internally managed bean s for post processor processing JPA annotations
    // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
    if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition();
        try {
            def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
                    AnnotationConfigUtils.class.getClassLoader()));
        } catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                    "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
        }
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
 
    // Register the internally managed bean used to process the post processor of @ EventListener annotation
    if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
    }
    // Register the EventListenerFactory object that internally manages the production of ApplicationListener objects
    if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
    }
 
    return beanDefs;
}

private static BeanDefinitionHolder registerPostProcessor(
        BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {
 
    // Set role
    definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    // Register BeanDefinition
    registry.registerBeanDefinition(beanName, definition);
    // Encapsulate into BeanDefinitionHolder and return
    return new BeanDefinitionHolder(definition, beanName);
}

 

Summary:

< context: component scan > the node resolution process is mainly divided into the following steps:

1. Scan the base package directory, take out all beans based on @ Component annotation and add them to the registry (these three important caches are still beanDefinitionMap, beanDefinitionNames and aliasMap, which are the same as the default namespace resolved by the parseDefaultElement call chain above)

2. Add several conventional bean postprocessors to BeanDefinition, such as autowirediannotationbeanpostprocessor based on @ Autowired.

        

Topics: Spring