欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于spring中bean注冊的優(yōu)先級分析

 更新時間:2024年09月19日 09:06:55   作者:潭影空人心  
Spring框架中,Bean的定義方式主要有三種:XML定義、注解掃描和配置類中的@Bean注解,在Bean注冊過程中,XML定義的GenericBeanDefinition優(yōu)先級最高

spring中bean注冊的優(yōu)先級

在 spring 中,我們知道,常見的定義 bean 的方式共有三種,xml 中 bean 標簽定義、component-scan 注解掃描定義、配置類中 @Bean 修飾方法定義。

這三種定義方式,也分別對應(yīng)三個 BeanDefinition 的實現(xiàn)類:

  • xml-bean:GenericBeanDefinition
  • component-scan:ScannedGenericBeanDefinition
  • @Bean:ConfigurationClassBeanDefinition

目前已知 xml-bean 優(yōu)先級最高

在 spring 中稱之為 top-level,下面來看看,spring 具體是如何實現(xiàn)這一 bean 注冊的優(yōu)先級的。

在 AbstractApplicationContext#refresh

執(zhí)行 obtainFreshBeanFactory 不但會創(chuàng)建一個 BeanFactory,還會對 xml 文件進行解析,加載注冊 BeanDefinition。

  • 如果 component-scan 標簽先于 xml-bean 標簽解析,就會先注冊一個 ScannedGenericBeanDefinition,待解析到 xml-bean 時,發(fā)現(xiàn)已經(jīng)存在,就會進行覆蓋。
  • 如果 xml-bean 標簽先于 component-scan 標簽解析,待解析到 component-scan 時,就會進行判斷。
// DefaultListableBeanFactory
@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);
		}
	}

	// 注冊時發(fā)現(xiàn)已經(jīng)存在
	// 除了 allowBeanDefinitionOverriding 為 false 時會拋出異常外,其余都會覆蓋
	BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
	if (existingDefinition != null) {
		// allowBeanDefinitionOverriding 默認 true,為 false 執(zhí)行到此直接拋出異常
		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 + "]");
			}
		}
		// equals 方法判定不相等,會覆蓋
		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 {
			// 仍在注冊階段
			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();
	}
}

可以看到,一旦執(zhí)行到 DefaultListableBeanFactory#registerBeanDefinition,就會對已經(jīng)存在的 BeanDefinition 進行覆蓋,因為 allowBeanDefinitionOverriding 默認為 true。

所以,如果想進行判斷,肯定是在 DefaultListableBeanFactory#registerBeanDefinition 執(zhí)行之前。

// ClassPathBeanDefinitionScanner
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) {
		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);
			if (candidate instanceof AbstractBeanDefinition) {
				postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
			}
			if (candidate instanceof AnnotatedBeanDefinition) {
				AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
			}
			// 返回 false,并不會進行 candidate 的注冊
			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;
}
// ClassPathBeanDefinitionScanner
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() + "]");
}

// ClassPathBeanDefinitionScanner
protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) {
	return (!(existingDefinition instanceof ScannedGenericBeanDefinition) ||  // explicitly registered overriding bean
			(newDefinition.getSource() != null && newDefinition.getSource().equals(existingDefinition.getSource())) ||  // scanned same file twice
			newDefinition.equals(existingDefinition));  // scanned equivalent class twice
}

可以看到,在 component-scan 時,在注冊之前,會調(diào)用 checkCandidate 進行判斷。如果不存在 beanName 對應(yīng)的 BeanDefinition,直接就返回 true 進行后續(xù)的注冊,否則委托給 isCompatible 進行判斷。如果是先注冊的 xml 封裝的 GenericBeanDefinition,再遇見掃描到的 ScannedGenericBeanDefinition,此時調(diào)用 isCompatible,existingDefinition 不是 ScannedGenericBeanDefinition 子類,返回 true,接著 checkCandidate 返回 false,并不會進行 BeanDefinition 的注冊。

接著再來看第三種,@Bean 定義的 BeanDefinition。

我們知道,在 AbstractApplicationContext#invokeBeanFactoryPostProcessors 時,會通過注冊的 ConfigurationClassPostProcessor 調(diào)用 processConfigBeanDefinitions 完成對配置類的解析以及配置類中 BeanDefinition 的注冊。而具體加載和注冊是通過 ConfigurationClassBeanDefinitionReader 來實現(xiàn)的,對于 @Bean 定義的方法,是通過 ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod 來處理的。在這個方法中,當確定了 beanName 之后,會調(diào)用 isOverriddenByExistingDefinition 來進行是否覆蓋的判斷。

// ConfigurationClassBeanDefinitionReader
// 返回 true,表明被已經(jīng)存的 BeanDefinition 覆蓋了,便不在執(zhí)行配置類中 Bean 的定義和注冊
protected boolean isOverriddenByExistingDefinition(BeanMethod beanMethod, String beanName) {
	if (!this.registry.containsBeanDefinition(beanName)) {
		return false;
	}
	BeanDefinition existingBeanDef = this.registry.getBeanDefinition(beanName);

	// 已經(jīng)存在 ConfigurationClassBeanDefinition,比較工廠名和工廠方法名,只要工廠名相同就會返回 true
	if (existingBeanDef instanceof ConfigurationClassBeanDefinition) {
		ConfigurationClassBeanDefinition ccbd = (ConfigurationClassBeanDefinition) existingBeanDef;
		if (ccbd.getMetadata().getClassName().equals(
				beanMethod.getConfigurationClass().getMetadata().getClassName())) {
			if (ccbd.getFactoryMethodMetadata().getMethodName().equals(ccbd.getFactoryMethodName())) {
				ccbd.setNonUniqueFactoryMethodName(ccbd.getFactoryMethodMetadata().getMethodName());
			}
			return true;
		}
		else {
			return false;
		}
	}

	// 通過 component-scan 掃描到的 BeanDefinition 會被 @Bean 定義的 BeanDefinition 覆蓋
	if (existingBeanDef instanceof ScannedGenericBeanDefinition) {
		return false;
	}

	// 比較角色,默認 0,即 ROLE_APPLICATION
	if (existingBeanDef.getRole() > BeanDefinition.ROLE_APPLICATION) {
		return false;
	}

	// 執(zhí)行到此,證明已經(jīng)存在的 BeanDefinition 是在配置類之前由 xml 定義的
	// DefaultListableBeanFactory 中 allowBeanDefinitionOverriding 默認 true,如果 allowBeanDefinitionOverriding 為 false,不允許覆蓋,出現(xiàn)了兩個 BeanDefinition 直接拋出異常
	if (this.registry instanceof DefaultListableBeanFactory &&
			!((DefaultListableBeanFactory) this.registry).isAllowBeanDefinitionOverriding()) {
		throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
				beanName, "@Bean definition illegally overridden by existing bean definition: " + existingBeanDef);
	}
	// 輸出被覆蓋的日志
	if (logger.isDebugEnabled()) {
		logger.debug(String.format("Skipping bean definition for %s: a definition for bean '%s' " +
				"already exists. This top-level bean definition is considered as an override.",
				beanMethod, beanName));
	}
	return true;
}

只有 isOverriddenByExistingDefinition 返回 false,程序才繼續(xù)向下執(zhí)行,會對當前 @Bean 定義的方法創(chuàng)建 ConfigurationClassBeanDefinition,之后執(zhí)行 registerBeanDefiniition。

可以看到,如果 existingBeanDef 是 ConfigurationClassBeanDefinition 類型,比較工廠名,相同,返回 true,不用當前 @Bean 進行覆蓋,否則返回 false,對當前 @Bean 定義的方法創(chuàng)建 ConfigurationClassBeanDefinition,對以前的 ConfigurationClassBeanDefinition 進行覆蓋。

如果 existingBeanDef 是 ScannedGenericBeanDefinition 類型,返回 false,創(chuàng)建 ConfigurationClassBeanDefinition 進行覆蓋。

當對 ConfigurationClassBeanDefinition 和 ScannedGenericBeanDefinition 都判斷完成后,剩下的 existingBeanDef 只能是 xml-bean 定義的 GenericBeanDefinition,此時 isOverriddenByExistingDefinition 返回 true,即針對 @Bean 定義 BeanDefinition 的注冊會終止,還是采用 existingBeanDef。

這就是為什么說 spring 中 xml 定義的 BeanDefinition 優(yōu)先級最高。這樣定義的好處就是,當 xml 發(fā)生變化后,只需重啟項目,不需要編譯代碼。

優(yōu)先級排序

如下:

GenericBeanDefinition > ConfigurationClassBeanDefinition > ScannedGenericBeanDefinition

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Maven配置文件pom.xml詳解

    Maven配置文件pom.xml詳解

    什么是POM?這篇文章主要介紹了Maven的配置文件pom.xml,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • 基于Java類的加載方式

    基于Java類的加載方式

    這篇文章主要介紹了基于Java類的加載方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Spring MVC請求參數(shù)與響應(yīng)結(jié)果全局加密和解密詳解

    Spring MVC請求參數(shù)與響應(yīng)結(jié)果全局加密和解密詳解

    這篇文章主要給大家介紹了關(guān)于Spring MVC請求參數(shù)與響應(yīng)結(jié)果全局加密和解密的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-08-08
  • springbooot使用google驗證碼的功能實現(xiàn)

    springbooot使用google驗證碼的功能實現(xiàn)

    這篇文章主要介紹了springbooot使用google驗證碼,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-05-05
  • 一文帶你徹底理解Java序列化和反序列化

    一文帶你徹底理解Java序列化和反序列化

    這篇文章主要介紹了Java序列化和反序列化的相關(guān)資料,幫助大家更好的理解和學習Java,感興趣的朋友可以了解下
    2020-09-09
  • 利用AOP實現(xiàn)系統(tǒng)告警的方法詳解

    利用AOP實現(xiàn)系統(tǒng)告警的方法詳解

    在開發(fā)的過程中會遇到各種各樣的開發(fā)問題,服務(wù)器宕機、網(wǎng)絡(luò)抖動、代碼本身的bug等等。針對代碼的bug,我們可以提前預(yù)支,通過發(fā)送告警信息來警示我們?nèi)ジ深A(yù),盡早處理。本文將利用AOP實現(xiàn)系統(tǒng)告警,需要的可以參考一下
    2022-09-09
  • MyBatis Generator 自定義生成注釋的方法

    MyBatis Generator 自定義生成注釋的方法

    這篇文章主要介紹了MyBatis Generator 自定義生成注釋的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09
  • IDEA配置tomcat的方法、IDEA配置tomcat運行web項目詳解

    IDEA配置tomcat的方法、IDEA配置tomcat運行web項目詳解

    這篇文章主要介紹了IDEA配置tomcat的方法、IDEA配置tomcat運行web項目詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • 利用Java多線程技術(shù)導(dǎo)入數(shù)據(jù)到Elasticsearch的方法步驟

    利用Java多線程技術(shù)導(dǎo)入數(shù)據(jù)到Elasticsearch的方法步驟

    這篇文章主要介紹了利用Java多線程技術(shù)導(dǎo)入數(shù)據(jù)到Elasticsearch的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-07-07
  • 最簡單的Spring Cloud教程第一篇:服務(wù)的注冊與發(fā)現(xiàn)(Eureka)

    最簡單的Spring Cloud教程第一篇:服務(wù)的注冊與發(fā)現(xiàn)(Eureka)

    這篇文章主要給大家介紹了關(guān)于Spring Cloud服務(wù)的注冊與發(fā)現(xiàn)(Eureka)的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用spring cloud具有一定的參考學習價值,需要的朋友們下面來一起看看吧。
    2017-08-08

最新評論