In the previous article , Package Scanning of Spring IoC Source Code Analysis (Annotation-based) , we introduced the process of Spring's process of scanning packages to obtain beans based on annotations. In this article, we will discuss the process of spring parsing beans and registering them with the IOC container.
Let's look at the following code first:
ClassPathBeanDefinitionScanner类
//类路径Bean定义扫描器扫描给定包及其子包
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
//创建一个集合,存放扫描到的BeanDefinition封装类
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
//遍历扫描所有给定的包路径
for (String basePackage : basePackages) {
//调用父类ClassPathScanningCandidateComponentProvider的方法
//扫描给定类路径,获取符合条件的Bean定义
10 Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
//遍历扫描到的Bean
for (BeanDefinition candidate : candidates) {
//获取@Scope注解的值,即获取Bean的作用域
14 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
//为Bean设置作用域
candidate.setScope(scopeMetadata.getScopeName());
//为Bean生成名称
18 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
//如果扫描到的Bean不是Spring的注解Bean,则为Bean设置默认值,
//设置Bean的自动依赖注入装配属性等
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
//如果扫描到的Bean是Spring的注解Bean,则处理其通用的Spring注解
if (candidate instanceof AnnotatedBeanDefinition) {
//处理注解Bean中通用的注解,在分析注解Bean定义类读取器时已经分析过
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
//根据Bean名称检查指定的Bean是否需要在容器中注册,或者在容器中冲突
30 if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
//根据注解中配置的作用域,为Bean应用相应的代理模式
33 definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//向容器注册扫描到的Bean
37 registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
In the last article, we mainly analyzed the findCandidateComponents(basePackage)
method This method mainly scans beans that meet the filtering rules from the given package path and stores them in the collection beanDefinitions
.
The next steps can be divided into the following aspects:
-
iterate over the bean collection
-
Get the value of the @Scope annotation, that is, get the scope of the bean
-
Generate a name for the bean
-
Set default values for some properties of beans
-
Check if the bean is registered in the IOC container
-
According to the scope of the bean, the corresponding proxy mode is generated
-
Put the Bean into the IOC container
The second step is to obtain the value of the @Scope annotation, that is, to obtain the scope of the bean
Let's first look at the process of obtainingScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
the bean scope, mainly the code in line 14 above , we continue to track it:
AnnotationScopeMetadataResolver class
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
//默认是singleton
ScopeMetadata metadata = new ScopeMetadata();
if (definition instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
//获取@Scope注解的值
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
annDef.getMetadata(), this.scopeAnnotationType);
//将获取到的@Scope注解的值设置到要返回的对象中
if (attributes != null) {
metadata.setScopeName(attributes.getString("value"));
//获取@Scope注解中的proxyMode属性值,在创建代理对象时会用到
ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
//如果@Scope的proxyMode属性为DEFAULT或者NO
if (proxyMode == ScopedProxyMode.DEFAULT) {
//设置proxyMode为NO
proxyMode = this.defaultProxyMode;
}
//为返回的元数据设置proxyMode
metadata.setScopedProxyMode(proxyMode);
}
}
//返回解析的作用域元信息对象
return metadata;
}
The scope of beans is @Scope
implemented through annotations. Let's first look at @Scope
the attributes of annotations:
You can see that the @Scope
annotation has three properties,
-
value
Attributes are the singleton/multiple instances we commonly use to set up Beans -
proxyMode
is used to set the proxy mode
For @Scope
a detailed analysis of the annotation principle, please see this article <>, which will not be discussed in detail here.
Here AnnotationAttributes
is the encapsulation class of the key-value annotation attribute, which is inherited LinkedHashMap
, so it is also a data structure in the form of key-value. Take a look at the value of this variable through debug:
It can be seen that the values of 3 attributes are obtained, among which value = prototype
is the scope of the bean. Here I set multiple instances, and then assign the obtained annotation attribute values to ScopeMetadata
.
The third step is to generate a name for the bean
Go back to line 18 in the doScan() method of the ClassPathBeanDefinitionScanner class above, and assign the scope of the Bean we obtained to the Bean.
Then generate a name for the bean, and the code String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
traces it in, in the AnnotationBeanNameGenerator class:
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
if (definition instanceof AnnotatedBeanDefinition) {
String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
if (StringUtils.hasText(beanName)) {
// Explicit bean name found.
return beanName;
}
}
// Fallback: generate a unique default bean name.
return buildDefaultBeanName(definition, registry);
}
This code is easy to understand. First get the name of the bean from the annotation. If there is no setting in the annotation, then generate a default bean name, by ClassUtils.getShortName(beanClassName)
generating a lowercase camel case name of the class name, such as studentController
.
The fourth step is to set default values for some properties of the Bean
Mainly the following two methods in doScan():
//如果扫描到的Bean不是Spring的注解Bean,则为Bean设置默认值,
//设置Bean的自动依赖注入装配属性等
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
//如果扫描到的Bean是Spring的注解Bean,则处理其通用的Spring注解
if (candidate instanceof AnnotatedBeanDefinition) {
//处理注解Bean中通用的注解,在分析注解Bean定义类读取器时已经分析过
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
First look postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName)
:
Tracking in will lead to the following methods:
public void applyDefaults(BeanDefinitionDefaults defaults) {
//懒加载
setLazyInit(defaults.isLazyInit());
//加载模式
setAutowireMode(defaults.getAutowireMode());
//依赖检查
setDependencyCheck(defaults.getDependencyCheck());
//bean初始化方法
setInitMethodName(defaults.getInitMethodName());
setEnforceInitMethod(false);
//bean销毁方法
setDestroyMethodName(defaults.getDestroyMethodName());
setEnforceDestroyMethod(false);
}
It should be clear here that setting some default values for the bean BeanDefinitionDefaults
is an encapsulation class for the default value of the bean attribute. The default value of each attribute is obtained from this class and assigned to the bean.
Then we look at the AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate)
method.
Follow in:
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
//如果Bean定义中有@Lazy注解,则将该Bean预实例化属性设置为@lazy注解的值
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"));
}
}
//如果Bean定义中有@Primary注解,则为该Bean设置为autowiring自动依赖注入装配的首选对象
if (metadata.isAnnotated(Primary.class.getName())) {
abd.setPrimary(true);
}
//如果Bean定义中有@ DependsOn注解,则为该Bean设置所依赖的Bean名称,
//容器将确保在实例化该Bean之前首先实例化所依赖的Bean
AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
if (dependsOn != null) {
abd.setDependsOn(dependsOn.getStringArray("value"));
}
if (abd instanceof AbstractBeanDefinition) {
AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd;
AnnotationAttributes role = attributesFor(metadata, Role.class);
if (role != null) {
absBd.setRole(role.getNumber("value").intValue());
}
AnnotationAttributes description = attributesFor(metadata, Description.class);
if (description != null) {
absBd.setDescription(description.getString("value"));
}
}
}
This is mainly to deal with some commonly used annotations on beans, such as @Lazy、@Primary、@DependsOn
.
The notes are very clear, so I won't go into details here.
The fifth step, check whether the bean has been registered in the IOC container
Trace the line 30 if (checkCandidate(beanName, candidate))
method in doScan():
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
//是否包含beanName了
if (!this.registry.containsBeanDefinition(beanName)) {
return true;
}
//如果容器中已经存在同名bean
//获取容器中已存在的bean
BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
if (originatingDef != null) {
existingDef = originatingDef;
}
//新bean旧bean进行比较
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() + "]");
}
It can be seen that it is actually by calling the containsBeanDefinition(beanName)
method of the IOC container to determine whether the beanName already exists, and the IOC container is actually one map
, and the bottom layer is actually implemented by calling map.containsKey(key)
.
The sixth step, apply the corresponding proxy mode for the bean
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
Trace methods in doScan()
static BeanDefinitionHolder applyScopedProxyMode(
ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
//获取注解Bean定义类中@Scope注解的proxyMode属性值
ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
//如果配置的@Scope注解的proxyMode属性值为NO,则不应用代理模式
if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
return definition;
}
//获取配置的@Scope注解的proxyMode属性值,如果为TARGET_CLASS
//则返回true,如果为INTERFACES,则返回false
boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
//为注册的Bean创建相应模式的代理对象
return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
}
Here we use the @Scope annotation properties obtained in the second stepproxyMode
, and then set the proxy mode for the bean.
The seventh step, register the bean to the IOC container
Trace the line 37 registerBeanDefinition(definitionHolder, this.registry);
method in doScan()
//将解析的BeanDefinitionHold注册到容器中
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
//获取解析的BeanDefinition的名称
String beanName = definitionHolder.getBeanName();
//向IOC容器注册BeanDefinition
第9行 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
//如果解析的BeanDefinition有别名,向容器为其注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
Look directly at the code on line 9 and continue to follow it:
//向IOC容器注册解析的BeanDefiniton
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// 校验 beanName 与 beanDefinition 非空
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
//校验解析的BeanDefiniton
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition oldBeanDefinition;
// 从容器中获取指定 beanName 的 BeanDefinition
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
// 如果已经存在
if (oldBeanDefinition != null) {
// 如果存在但是不允许覆盖,抛出异常
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
// 覆盖 beanDefinition 大于 被覆盖的 beanDefinition 的 ROLE ,打印 info 日志
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(oldBeanDefinition)) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
// 允许覆盖,直接覆盖原有的 BeanDefinition 到 beanDefinitionMap 中。
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
// 检测创建 Bean 阶段是否已经开启,如果开启了则需要对 beanDefinitionMap 进行并发控制
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
//注册的过程中需要线程同步,以保证数据的一致性(因为有put、add、remove操作)
64 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;
// 从 manualSingletonNames 移除 beanName
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
//检查是否有同名的BeanDefinition已经在IOC容器中注册
88 if (oldBeanDefinition != null || containsSingleton(beanName)) {
//更新beanDefinitionNames 和 manualSingletonNames
resetBeanDefinition(beanName);
}
}
Here is the core code for registering beans in the IOC container. This code is very long. Let's look at it separately and mainly divide it into several steps:
-
beanName
beanDefinition
The legality check of the sum -
Judging whether it has been registered according to
beanName
the IOC container Judging whether to overwrite
according to the variableisAllowBeanDefinitionOverriding
-
If there is according to the coverage rule, execute the coverage or throw an exception
-
If it does not exist, put it into the IOC
beanDefinitionMap
container
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
At this point, the process of registering beans to the IOC container is basically over. In fact, IOC registration is not a mysterious thing. To put it bluntly, it is to store beanName
and bean
into the map collection.
At this point, we go back to the method of the code class in step 7. We can see that we have finished the analysis, and the rest is to register the alias of the bean and you are done.BeanDefinitionReaderUtils
registerBeanDefinition()
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
//将解析的BeanDefinitionHold注册到容器中
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
//获取解析的BeanDefinition的名称
String beanName = definitionHolder.getBeanName();
//向IOC容器注册BeanDefinition
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
//如果解析的BeanDefinition有别名,向容器为其注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
Summarize:
The IoC container is actually DefaultListableBeanFactory
, it has a map
type beanDefinitionMap
variable in it to store the registered bean
IoC container initialization process :
-
1. Resource location
Scan the.class
files under the package path and convert the resources toResource
-
2. Resource loading
Obtain class metadata through the ASM framework and encapsulate it intoBeanDefinition
-
3. Resource resolution
Get the attribute value of the annotation on the bean. Such as@Scope
-
4. Generate Bean
generationbeanName
, set Bean default values (lazy loading, initialization methods, etc.), proxy mode -
5. Register the Bean
and putBeanDefinition
it into the IoC containerDefaultListableBeanFactory
A few final questions to consider :
-
beanDefinitionMap
It is aConcurrentHashMap
type and should be thread-safe, but why add a sync lock in the 64th line of code? -
You have already checked whether there are beans with the same name in the container. Why do you need to check again on line 88?