[Spring Topic] Spring's Bean Life Cycle Source Code Analysis - Phase 1 (Scanning to Generate BeanDefinition)

foreword

reading preparation

Since the Spring source code analysis is a process with a strong front-to-back relationship, and the analysis here is also explained in the order of the code, so if you do not understand the pre-knowledge, there is a high probability that you will not be able to understand the current content. Therefore, it is especially recommended to read my previous articles (from top to bottom):

(PS: Especially "Bean's Life Cycle Flowchart", to help you [open your eyes] and understand the process first. After all, [understanding the code through the business is much easier than understanding the business through the code]!!!!)
(PS: Especially "Bean's Life Cycle Flowchart", to help you [open your eyes] and understand the process first. After all, [understanding the code through the business is much easier than understanding the business through the code]!!!!)
(PS: Especially "Bean's Life Cycle Flowchart", to help you [open your eyes] and understand the process first. After all, [understanding the code through the business is much easier than understanding the business through the code]!!!!)

reading guide

The most important function of Spring is to help programmers create objects (that is, IOC), and starting Spring is to prepare for the creation of Bean objects, so we first understand how Spring creates Beans, that is, first understand the life cycle of Beans .
The life cycle of a bean refers to how a bean is generated and destroyed in Spring.
The content of this lesson will be explained with the following code as the entry point:

public class MyApplicationTest {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("org.tuling.spring");
        Object newUser = context.getBean("newUser");
        System.out.println(newUser);
    }
}

The main use is the construction method of the following Spring container:

/**
 * 创建一个新的AnnotationConfigApplicationContext,扫描给定包中的组件,为这些组件注册bean定义,并自动刷新上下文。
 * 参数:
 * baseppackages——要扫描组件类的包
 */
public AnnotationConfigApplicationContext(String... basePackages) {
    
    
	this();
	scan(basePackages);
	refresh();
}

reading advice

  1. Look at the source code, remember to get entangled in the details, otherwise it is easy to get stuck. Normally, just look at the main process
  2. If you don't understand, look at class annotations or method annotations. Spring is such an excellent source code, the annotations are really in place
  3. If you are an idea user, use the bookmark function of F11 more.
    • Ctrl + F11 Select files/folders, use mnemonics to set/cancel bookmarks (required)
    • Shift + F11 Pop-up bookmark display layer (required)
    • Ctrl +1,2,3...9 Locate to the bookmark position of the corresponding value (required)

Course content

1. Generate BeanDefinition

1.1 Brief review

If you have read my previous article, you should know that BeanDefinitonthe generation of beans is in the [scanning] stage before producing beans. As shown below:
insert image description here
insert image description here

But we also said that this is just a simple implementation. In fact, Spring's implementation of this scanning process involves 3 core classes and 10 core methods!

*1.2 Concept review

In this [scanning] process, some concepts of Spring’s underlying design were involved. I introduced it in the last note. For those who don’t remember it, remember to go back and look it up.
The main concepts involved are:

  • BeanDefinition (design drawing): BeanDefinition represents the Bean definition, and there are many attributes in BeanDefinition to describe the characteristics of a Bean
  • ClassPathBeanDefinitionScanner (sheet register): used to register sheets
  • BeanFacotory (Bean factory): Produce Bean. But in fact, the registration capability of the interface DefaultListableBeanFactoryof this class is used hereBeanDefinitionRegistry

Article link:
" [Spring Topic] Analysis of the Core Concepts of Spring's Underlying Architecture "

1.3 Explanation of core methods

As we mentioned earlier, the entire scanning process will involve [3 core classes, 10 core methods]. Below, we will explain each method in turn according to the calling order.

2. Method explanation

The entry of the entire scanning process is the method in the following code scan(), and the final call isClassPathBeanDefinitionScanner.scan()

/**
 * 创建一个新的AnnotationConfigApplicationContext,扫描给定包中的组件,为这些组件注册bean定义,并自动刷新上下文。
 * 参数:
 * baseppackages——要扫描组件类的包
 */
public AnnotationConfigApplicationContext(String... basePackages) {
    
    
	this();
	scan(basePackages);
	refresh();
}

public void scan(String... basePackages) {
    
    
	Assert.notEmpty(basePackages, "At least one base package must be specified");
	StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan")
			.tag("packages", () -> Arrays.toString(basePackages));
	this.scanner.scan(basePackages);
	scanPackages.end();
}

2.1 ClassPathBeanDefinitionScanner#scan

Full path: org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan
method annotation: perform scanning in the specified basic package

The source code is as follows:

	public int scan(String... basePackages) {
    
    
		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

		doScan(basePackages);

		// Register annotation config processors, if necessary.
		if (this.includeAnnotationConfig) {
    
    
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
		}

		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
	}

But in fact, the real work is not here, but insidedoScan()

2.2 ClassPathBeanDefinitionScanner#doScan

Method call chain: called by scan() in 2.1
Full path: org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan
method annotation: scan in the specified base package and return the registered bean definition. This method does not register an annotation configuration handler, but leaves this job to the caller.

The source code is as follows:

    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    
    
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
        for (String basePackage : basePackages) {
    
    
            
            // 寻找候选的BeanDefinition
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            for (BeanDefinition candidate : candidates) {
    
    
                ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                candidate.setScope(scopeMetadata.getScopeName());
                String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                
                // 判断是否要设置BeanDefinition属性
                if (candidate instanceof AbstractBeanDefinition) {
    
    
                    postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
                }
                
                // 判断是否要设置通用的注解BeanDefiniton属性
                if (candidate instanceof AnnotatedBeanDefinition) {
    
    
                    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
                }
                
                // 判断是否注册BeanDefinition
                if (checkCandidate(beanName, candidate)) {
    
    
                    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                    definitionHolder =
                            AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                    beanDefinitions.add(definitionHolder);
                    registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }
        return beanDefinitions;
    }

Interpretation of the method: This code seems to be a lot, but in fact it is relatively simple as a whole, divided into 4 steps

  1. First scan all the classes under the classpath to find the candidate BeanDefinition. As for what is a candidate, it will be explained later
  2. Traverse the found candidate BeanDefinitions to determine whether some properties of the BeanDefinition need to be set. such as default properties
  3. Determine whether to set the general annotation BeanDefiniton attribute
  4. Determine whether to register BeanDefinition, after all, there may be duplicate BeanDefinition

2.3 ClassPathScanningCandidateComponentProvider#findCandidateComponents

Method call chain: called by doScan() in 2.2
Full path: org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents
method annotation: scan candidate Components on the class path.

The source code is as follows:

	public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    
    
		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
    
    
			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
		}
		else {
    
    
			return scanCandidateComponents(basePackage);
		}
	}

Interpretation of the method: There is one here if-else, but it is usually entered else. ifWe will basically not use the situation inside in business development. This is a strategy to speed up the scanning of packages by using the idea of ​​​​indexing. How to achieve it? It is to create a new configuration file (KV format) resourcebelow . spring.componentsas follows:

org.tuling.spring.bean.User = org.springframework.stereotype.Component

By directly telling Spring which are candidate Components, the process of scanning all class files and screening is eliminated, thereby improving efficiency.

2.4 ClassPathScanningCandidateComponentProvider#scanCandidateComponents

Method call chain: called by findCandidateComponents() in 2.3
Full path: org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents
method annotation: scan candidate components on the class path.

The source code is as follows:

    private Set<org.springframework.beans.factory.config.BeanDefinition> scanCandidateComponents(String basePackage) {
    
    
        Set<BeanDefinition> candidates = new LinkedHashSet<>();
        try {
    
    
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    resolveBasePackage(basePackage) + '/' + this.resourcePattern;
            Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
            boolean traceEnabled = logger.isTraceEnabled();
            boolean debugEnabled = logger.isDebugEnabled();
            for (Resource resource : resources) {
    
    
                if (traceEnabled) {
    
    
                    logger.trace("Scanning " + resource);
                }
                try {
    
    
                    
                    // 读取类元信息
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                    
                    // 第一层判断是否为候选组件,判断核心为:是否需要过滤
                    if (isCandidateComponent(metadataReader)) {
    
    
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setSource(resource);
                        
                        // 第二层判断是否为候选组件,判断核心为:是否独立、抽象类等
                        if (isCandidateComponent(sbd)) {
    
    
                            if (debugEnabled) {
    
    
                                logger.debug("Identified candidate component class: " + resource);
                            }
                            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 (FileNotFoundException ex) {
    
    
                    if (traceEnabled) {
    
    
                        logger.trace("Ignored non-readable " + resource + ": " + ex.getMessage());
                    }
                }
                catch (Throwable ex) {
    
    
                    throw new BeanDefinitionStoreException(
                            "Failed to read candidate component class: " + resource, ex);
                }
            }
        }
        catch (IOException ex) {
    
    
            throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
        }
        return candidates;
    }

Interpretation of the method: The code looks very long, but it is actually very simple. Mainly divided into 4 steps

  1. Read classifier information (read from disk)
    • First, obtain all .class files under the specified package path through ResourcePatternResolver (this file is packaged into a Resource object in the Spring source code)
    • Iterate through each Resource object
    • Use MetadataReaderFactory to parse the Resource object to get MetadataReader (in the Spring source code, the specific implementation class of MetadataReaderFactory is CachingMetadataReaderFactory, and the specific implementation class of MetadataReader is SimpleMetadataReader)
  2. To judge whether it is a candidate component, the judgment core is: whether to filter (the first one isCandidateComponent)
    • Use MetadataReader to filter excludeFilters and includeFilters, as well as conditional annotation @Conditional (conditional annotation does not understand: whether there is a @Conditional annotation on a certain class, if it exists, call the match method of the class specified in the annotation to match, and if the match is successful Pass the filter, and pass if the match fails.)
    • After passing the screening, generate ScannedGenericBeanDefinition based on metadataReader
  3. To judge whether it is a candidate component, the judgment core is: whether it is independent, abstract class, etc. (Second ) (Note: It is not the same thing isCandidateComponentas above , it belongs to method overloading)isCandidateComponent
  4. If the screening is passed, it means that a Bean has been scanned, and ScannedGenericBeanDefinition is added to the result set

MetadataReader represents the metadata reader of the class, mainly including an AnnotationMetadata, with functions

  • get the name of the class,
  • Get the name of the parent class
  • Get all implemented interface names
  • Get the names of all inner classes
  • Judging whether it is an abstract class
  • Judging whether it is an interface
  • Determine whether it is an annotation
  • Get the set of methods with an annotation
  • Get all annotation information added on the class
  • Get the collection of all annotation types added on the class

2.5 ClassPathScanningCandidateComponentProvider#isCandidateComponent

Method call chain: Called by scanCandidateComponents() in 2.4
Full path: org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent
Method annotation: Determines whether a given class does not match any exclusion filters and matches at least one inclusion filter .

The source code is as follows:

	protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    
    
		for (TypeFilter tf : this.excludeFilters) {
    
    
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
    
    
				return false;
			}
		}
		for (TypeFilter tf : this.includeFilters) {
    
    
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
    
    
				return isConditionMatch(metadataReader);
			}
		}
		return false;
	}

Interpretation of the method: This is just a verification, whether the current class is within the filter exclusion options. I guess many of my friends think of the attributes @ComponentScanin the annotations . It can only be said that it is not completely correct. Because, when we start the container creation, the default filtering policy will also be added. As follows:includeFilterexcludeFilter
ClassPathBeanDefinitionScanner

/**
 * 注册@Component的默认过滤器。
 * 这将隐式注册所有带有@Component元注释的注释,包括@Repository、@Service和@Controller构造型注释。
 * 还支持Java EE 6的javax.annotation.ManagedBean和JSR-330的javax.inject.Named注释(如果可用)。
 */
protected void registerDefaultFilters() {
    
    
	this.includeFilters.add(new AnnotationTypeFilter(Component.class));
	ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
	try {
    
    
		this.includeFilters.add(new AnnotationTypeFilter(
				((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
		logger.trace("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 {
    
    
		this.includeFilters.add(new AnnotationTypeFilter(
				((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
		logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
	}
	catch (ClassNotFoundException ex) {
    
    
		// JSR-330 API not available - simply skip.
	}
}

2.6 ClassPathScanningCandidateComponentProvider#isCandidateComponent

Method call chain: called by scanCandidateComponents() in 2.4
Full path: org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent
method annotation: Determine whether a given bean definition meets the candidate conditions. The default implementation checks that the class is not an interface and depends on the enclosing class.

The source code is as follows:

/**
 *确定给定的bean定义是否符合候选条件。
 * 默认实现检查类是否不是接口,是否依赖于封闭类。
 * 可以在子类中重写。
 */
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    
    
	AnnotationMetadata metadata = beanDefinition.getMetadata();
	return (metadata.isIndependent() && (metadata.isConcrete() ||
			(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}

Method Interpretation: There are two relatively unfamiliar ones here, that is, what is an independent class, and Lookup annotations.

  1. Independent class, and not an ordinary inner class (static inner class is an independent class)
  2. Lookup annotation, go to Baidu to use it yourself

To sum up the method, the conditions for judging a candidate Component are:

  1. The first is the independent class
  2. Either a specific subclass, or: an abstract class with Lookup annotations

2.7 ClassPathBeanDefinitionScanner#postProcessBeanDefinition

Method call chain: called by doScan() in 2.2
Full path: org.springframework.context.annotation.ClassPathBeanDefinitionScanner#postProcessBeanDefinition
method annotation: scan candidate Components on the classpath.

The source code is as follows:

/**
 * 除了通过扫描组件类检索到的内容之外,还可以对给定的bean定义应用进一步的设置。
 */
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
    
    
		beanDefinition.applyDefaults(this.beanDefinitionDefaults);
		if (this.autowireCandidatePatterns != null) {
    
    
			beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
		}
	}

Interpretation of the method: Here, some default properties of BeanDefinition are mainly initialized. beanDefinition.applyDefaults(this.beanDefinitionDefaults);
The source code is as follows:

	public void applyDefaults(BeanDefinitionDefaults defaults) {
    
    
		Boolean lazyInit = defaults.getLazyInit();
		if (lazyInit != null) {
    
    
			setLazyInit(lazyInit);
		}
		setAutowireMode(defaults.getAutowireMode());
		setDependencyCheck(defaults.getDependencyCheck());
		setInitMethodName(defaults.getInitMethodName());
		setEnforceInitMethod(false);
		setDestroyMethodName(defaults.getDestroyMethodName());
		setEnforceDestroyMethod(false);
	}

2.8 AnnotationConfigUtils.processCommonDefinitionAnnotations

Method call chain: called by doScan() in 2.2
Full path: org.springframework.context.annotation.AnnotationConfigUtils#processCommonDefinitionAnnotations
Method annotation: Set BeanDefinition annotation-related properties according to the accumulated class meta information of annotations.

The source code is as follows:

public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
    
    
	processCommonDefinitionAnnotations(abd, abd.getMetadata());
}
	
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
    
    
		AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
		if (lazy != null) {
    
    
			abd.setLazyInit(lazy.getBoolean("value"));
		}
		else if (abd.getMetadata() != metadata) {
    
    
			lazy = attributesFor(abd.getMetadata(), Lazy.class);
			if (lazy != null) {
    
    
				abd.setLazyInit(lazy.getBoolean("value"));
			}
		}

		if (metadata.isAnnotated(Primary.class.getName())) {
    
    
			abd.setPrimary(true);
		}
		AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
		if (dependsOn != null) {
    
    
			abd.setDependsOn(dependsOn.getStringArray("value"));
		}

		AnnotationAttributes role = attributesFor(metadata, Role.class);
		if (role != null) {
    
    
			abd.setRole(role.getNumber("value").intValue());
		}
		AnnotationAttributes description = attributesFor(metadata, Description.class);
		if (description != null) {
    
    
			abd.setDescription(description.getString("value"));
		}
	}

Nothing to interpret, very simple judgment

2.9 ClassPathBeanDefinitionScanner#checkCandidate

Method call chain: called by doScan() in 2.2
Full path: org.springframework.context.annotation.ClassPathBeanDefinitionScanner#checkCandidate
Method annotation: Check the given candidate bean name to determine whether the corresponding bean definition needs to be registered, or whether it is related to Existing definitions conflict.

The source code is as follows:

protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
    
    
		if (!this.registry.containsBeanDefinition(beanName)) {
    
    
			return true;
		}
		BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
		BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
		if (originatingDef != null) {
    
    
			existingDef = originatingDef;
		}
		if (isCompatible(beanDefinition, existingDef)) {
    
    
			return false;
		}
		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() + "]");
	}

Interpretation of the method: Normally speaking, either returns true, or repeatedly defines the same name Beanand then throws an exception. Return directly false, usually when there are parent-child containers.

2.10 DefaultListableBeanFactory#registerBeanDefinition

Method call chain: called by doScan() in 2.2
Full path: org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
method comment: use this registration center to register a new bean definition. Must support RootBeanDefinition and ChildBeanDefinition.

The source code is as follows:

@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
    
    

		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

		if (beanDefinition instanceof AbstractBeanDefinition) {
    
    
			try {
    
    
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
    
    
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		if (existingDefinition != null) {
    
    
			if (!isAllowBeanDefinitionOverriding()) {
    
    
				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
			else if (existingDefinition.getRole() < beanDefinition.getRole()) {
    
    
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				if (logger.isInfoEnabled()) {
    
    
					logger.info("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							existingDefinition + "] with [" + beanDefinition + "]");
				}
			}
			else if (!beanDefinition.equals(existingDefinition)) {
    
    
				if (logger.isDebugEnabled()) {
    
    
					logger.debug("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			else {
    
    
				if (logger.isTraceEnabled()) {
    
    
					logger.trace("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
    
    
			if (hasBeanCreationStarted()) {
    
    
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {
    
    
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					removeManualSingletonName(beanName);
				}
			}
			else {
    
    
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				removeManualSingletonName(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		if (existingDefinition != null || containsSingleton(beanName)) {
    
    
			resetBeanDefinition(beanName);
		}
		else if (isConfigurationFrozen()) {
    
    
			clearByTypeCache();
		}
	}

Interpretation of the method: This is a little more complicated, and I haven't looked at it carefully. Moreover, this is hidden very deep. Although it is doScan()called from inside, it is essentially calling the method AnnotationConfigApplicationContextinherited GenericApplicationContextfromregisterBeanDefinition

3. Scan logic flow chart

insert image description here

  1. First, obtain all class files under the specified package path through ResourcePatternResolver, and package them into Resource
  2. Traverse each Resource object
  3. Use ASM technology to parse each file, get the corresponding class meta information, and encapsulate it into MetadataReader
  4. Use excludeFilter, includeFilter, and @Conditionnal annotations for filtering
  5. After passing the screening, generate ScannedGenericBeanDefinition based on metadataReader
  6. Then judge whether the corresponding class is an independent class, interface or abstract class based on metadataReader
  7. If the screening is passed, it means that a Bean has been scanned, and ScannedGenericBeanDefinition is added to the result set

4. Merge BeanDefinition

In the subsequent IOC process, when generating beans, we will see many calls like this:

RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);

This is the so-called, get merged BeanDefinition. What is a merged BeanDefinition? In fact, it is the father and son BeanDefinition. It is the same as inheritance in Java. However, this usually happens in the xml configuration bean, as follows:

<bean id="parent" class="com.zhouyu.service.Parent" scope="prototype"/>
<bean id="child" class="com.zhouyu.service.Child"/>

But in the case of such a definition, the child is the prototype Bean. Because child's parent BeanDefinition is parent, it will inherit the scope attribute defined on parent. Before generating the Bean object according to the child, it is necessary to merge the BeanDefinition to obtain the complete BeanDefinition of the child.

summarize

  1. Learned the underlying principle of [scanning] in the Spring source code process

Guess you like

Origin blog.csdn.net/qq_32681589/article/details/132208387