關(guān)于spring中bean注冊(cè)的優(yōu)先級(jí)分析
spring中bean注冊(cè)的優(yōu)先級(jí)
在 spring 中,我們知道,常見(jiàn)的定義 bean 的方式共有三種,xml 中 bean 標(biāo)簽定義、component-scan 注解掃描定義、配置類中 @Bean 修飾方法定義。
這三種定義方式,也分別對(duì)應(yīng)三個(gè) BeanDefinition 的實(shí)現(xiàn)類:
- xml-bean:GenericBeanDefinition
- component-scan:ScannedGenericBeanDefinition
- @Bean:ConfigurationClassBeanDefinition
目前已知 xml-bean 優(yōu)先級(jí)最高
在 spring 中稱之為 top-level,下面來(lái)看看,spring 具體是如何實(shí)現(xiàn)這一 bean 注冊(cè)的優(yōu)先級(jí)的。
在 AbstractApplicationContext#refresh
執(zhí)行 obtainFreshBeanFactory 不但會(huì)創(chuàng)建一個(gè) BeanFactory,還會(huì)對(duì) xml 文件進(jìn)行解析,加載注冊(cè) BeanDefinition。
- 如果 component-scan 標(biāo)簽先于 xml-bean 標(biāo)簽解析,就會(huì)先注冊(cè)一個(gè) ScannedGenericBeanDefinition,待解析到 xml-bean 時(shí),發(fā)現(xiàn)已經(jīng)存在,就會(huì)進(jìn)行覆蓋。
- 如果 xml-bean 標(biāo)簽先于 component-scan 標(biāo)簽解析,待解析到 component-scan 時(shí),就會(huì)進(jìn)行判斷。
// 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);
}
}
// 注冊(cè)時(shí)發(fā)現(xiàn)已經(jīng)存在
// 除了 allowBeanDefinitionOverriding 為 false 時(shí)會(huì)拋出異常外,其余都會(huì)覆蓋
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
// allowBeanDefinitionOverriding 默認(rèn) true,為 false 執(zhí)行到此直接拋出異常
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
// 已存在的角色等級(jí)低,會(huì)覆蓋
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 方法判定不相等,會(huì)覆蓋
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 {
// 仍在注冊(cè)階段
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,就會(huì)對(duì)已經(jīng)存在的 BeanDefinition 進(jìn)行覆蓋,因?yàn)?allowBeanDefinitionOverriding 默認(rèn)為 true。
所以,如果想進(jìn)行判斷,肯定是在 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,并不會(huì)進(jìn)行 candidate 的注冊(cè)
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 時(shí),在注冊(cè)之前,會(huì)調(diào)用 checkCandidate 進(jìn)行判斷。如果不存在 beanName 對(duì)應(yīng)的 BeanDefinition,直接就返回 true 進(jìn)行后續(xù)的注冊(cè),否則委托給 isCompatible 進(jìn)行判斷。如果是先注冊(cè)的 xml 封裝的 GenericBeanDefinition,再遇見(jiàn)掃描到的 ScannedGenericBeanDefinition,此時(shí)調(diào)用 isCompatible,existingDefinition 不是 ScannedGenericBeanDefinition 子類,返回 true,接著 checkCandidate 返回 false,并不會(huì)進(jìn)行 BeanDefinition 的注冊(cè)。
接著再來(lái)看第三種,@Bean 定義的 BeanDefinition。
我們知道,在 AbstractApplicationContext#invokeBeanFactoryPostProcessors 時(shí),會(huì)通過(guò)注冊(cè)的 ConfigurationClassPostProcessor 調(diào)用 processConfigBeanDefinitions 完成對(duì)配置類的解析以及配置類中 BeanDefinition 的注冊(cè)。而具體加載和注冊(cè)是通過(guò) ConfigurationClassBeanDefinitionReader 來(lái)實(shí)現(xiàn)的,對(duì)于 @Bean 定義的方法,是通過(guò) ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod 來(lái)處理的。在這個(gè)方法中,當(dāng)確定了 beanName 之后,會(huì)調(diào)用 isOverriddenByExistingDefinition 來(lái)進(jìn)行是否覆蓋的判斷。
// ConfigurationClassBeanDefinitionReader
// 返回 true,表明被已經(jīng)存的 BeanDefinition 覆蓋了,便不在執(zhí)行配置類中 Bean 的定義和注冊(cè)
protected boolean isOverriddenByExistingDefinition(BeanMethod beanMethod, String beanName) {
if (!this.registry.containsBeanDefinition(beanName)) {
return false;
}
BeanDefinition existingBeanDef = this.registry.getBeanDefinition(beanName);
// 已經(jīng)存在 ConfigurationClassBeanDefinition,比較工廠名和工廠方法名,只要工廠名相同就會(huì)返回 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;
}
}
// 通過(guò) component-scan 掃描到的 BeanDefinition 會(huì)被 @Bean 定義的 BeanDefinition 覆蓋
if (existingBeanDef instanceof ScannedGenericBeanDefinition) {
return false;
}
// 比較角色,默認(rèn) 0,即 ROLE_APPLICATION
if (existingBeanDef.getRole() > BeanDefinition.ROLE_APPLICATION) {
return false;
}
// 執(zhí)行到此,證明已經(jīng)存在的 BeanDefinition 是在配置類之前由 xml 定義的
// DefaultListableBeanFactory 中 allowBeanDefinitionOverriding 默認(rèn) true,如果 allowBeanDefinitionOverriding 為 false,不允許覆蓋,出現(xiàn)了兩個(gè) 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í)行,會(huì)對(duì)當(dāng)前 @Bean 定義的方法創(chuàng)建 ConfigurationClassBeanDefinition,之后執(zhí)行 registerBeanDefiniition。
可以看到,如果 existingBeanDef 是 ConfigurationClassBeanDefinition 類型,比較工廠名,相同,返回 true,不用當(dāng)前 @Bean 進(jìn)行覆蓋,否則返回 false,對(duì)當(dāng)前 @Bean 定義的方法創(chuàng)建 ConfigurationClassBeanDefinition,對(duì)以前的 ConfigurationClassBeanDefinition 進(jìn)行覆蓋。
如果 existingBeanDef 是 ScannedGenericBeanDefinition 類型,返回 false,創(chuàng)建 ConfigurationClassBeanDefinition 進(jìn)行覆蓋。
當(dāng)對(duì) ConfigurationClassBeanDefinition 和 ScannedGenericBeanDefinition 都判斷完成后,剩下的 existingBeanDef 只能是 xml-bean 定義的 GenericBeanDefinition,此時(shí) isOverriddenByExistingDefinition 返回 true,即針對(duì) @Bean 定義 BeanDefinition 的注冊(cè)會(huì)終止,還是采用 existingBeanDef。
這就是為什么說(shuō) spring 中 xml 定義的 BeanDefinition 優(yōu)先級(jí)最高。這樣定義的好處就是,當(dāng) xml 發(fā)生變化后,只需重啟項(xiàng)目,不需要編譯代碼。
優(yōu)先級(jí)排序
如下:
GenericBeanDefinition > ConfigurationClassBeanDefinition > ScannedGenericBeanDefinition
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring MVC請(qǐng)求參數(shù)與響應(yīng)結(jié)果全局加密和解密詳解
這篇文章主要給大家介紹了關(guān)于Spring MVC請(qǐng)求參數(shù)與響應(yīng)結(jié)果全局加密和解密的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08
springbooot使用google驗(yàn)證碼的功能實(shí)現(xiàn)
這篇文章主要介紹了springbooot使用google驗(yàn)證碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05
利用AOP實(shí)現(xiàn)系統(tǒng)告警的方法詳解
在開(kāi)發(fā)的過(guò)程中會(huì)遇到各種各樣的開(kāi)發(fā)問(wèn)題,服務(wù)器宕機(jī)、網(wǎng)絡(luò)抖動(dòng)、代碼本身的bug等等。針對(duì)代碼的bug,我們可以提前預(yù)支,通過(guò)發(fā)送告警信息來(lái)警示我們?nèi)ジ深A(yù),盡早處理。本文將利用AOP實(shí)現(xiàn)系統(tǒng)告警,需要的可以參考一下2022-09-09
IDEA配置tomcat的方法、IDEA配置tomcat運(yùn)行web項(xiàng)目詳解
這篇文章主要介紹了IDEA配置tomcat的方法、IDEA配置tomcat運(yùn)行web項(xiàng)目詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
利用Java多線程技術(shù)導(dǎo)入數(shù)據(jù)到Elasticsearch的方法步驟
這篇文章主要介紹了利用Java多線程技術(shù)導(dǎo)入數(shù)據(jù)到Elasticsearch的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07
最簡(jiǎn)單的Spring Cloud教程第一篇:服務(wù)的注冊(cè)與發(fā)現(xiàn)(Eureka)
這篇文章主要給大家介紹了關(guān)于Spring Cloud服務(wù)的注冊(cè)與發(fā)現(xiàn)(Eureka)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用spring cloud具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-08-08

