Spring IoC source code analysis (annotation-based) Bean parsing and registration

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 @Scopeimplemented through annotations. Let's first look at @Scopethe attributes of annotations:


You can see that the @Scopeannotation has three properties,

  • valueAttributes are the singleton/multiple instances we commonly use to set up Beans

  • proxyModeis used to set the proxy mode

For @Scopea detailed analysis of the annotation principle, please see this article <>, which will not be discussed in detail here.

Here AnnotationAttributesis 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 = prototypeis 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 bean name from the annotation, if not set in the annotation, then generate a default bean name, by ClassUtils.getShortName(beanClassName)generating a lowercase camel case name of the class name, eg 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 BeanDefinitionDefaultsis 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:

  • beanNamebeanDefinitionThe legality check of the sum

  • Judging whether it has been registered according to beanNamethe 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 beanDefinitionMapcontainer
    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 beanNameand beaninto 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.BeanDefinitionReaderUtilsregisterBeanDefinition()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 maptype beanDefinitionMapvariable in it to store the registered bean

IoC container initialization process :

  • 1. Resource location
    Scan the .classfiles 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
    generation beanName, set Bean default values ​​(lazy loading, initialization methods, etc.), proxy mode

  • 5. Register the Bean
    and put BeanDefinitionit into the IoC containerDefaultListableBeanFactory

A few final questions to consider :

  • beanDefinitionMapIt is a ConcurrentHashMaptype 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?

{{o.name}}
{{m.name}}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324080156&siteId=291194637