国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【Spring專題】Spring之Bean的生命周期源碼解析——階段一(掃描生成BeanDefinition)

這篇具有很好參考價值的文章主要介紹了【Spring專題】Spring之Bean的生命周期源碼解析——階段一(掃描生成BeanDefinition)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

前言

閱讀準備

由于Spring源碼分析是一個前后聯(lián)系比較強的過程,而且這邊分析,也是按照代碼順序講解的,所以不了解前置知識的情況下,大概率沒辦法看懂當前的內(nèi)容。所以,特別推薦看看我前面的文章(自上而下次序):

  • Spring底層核心原理解析——引導篇【學習難度:★★☆☆☆
  • 手寫簡易Spring容器過程分析——引導篇【學習難度:★★☆☆☆
  • Spring之底層架構(gòu)核心概念解析【學習難度:★★★☆☆,重要程度:★★★★★
  • Bean的生命周期流程圖【學習難度:☆☆☆☆☆,重要程度:★★★★★

(PS:特別是《Bean的生命周期流程圖》,幫大家【開天眼】,先了解下流程。畢竟【通過業(yè)務了解代碼,遠比通過代碼了解業(yè)務簡單的多】?。。。。?/mark>
(PS:特別是《Bean的生命周期流程圖》,幫大家【開天眼】,先了解下流程。畢竟【通過業(yè)務了解代碼,遠比通過代碼了解業(yè)務簡單的多】?。。。。?/mark>
(PS:特別是《Bean的生命周期流程圖》,幫大家【開天眼】,先了解下流程。畢竟【通過業(yè)務了解代碼,遠比通過代碼了解業(yè)務簡單的多】?。。。。?/mark>

閱讀指引

Spring最重要的功能就是幫助程序員創(chuàng)建對象(也就是IOC),而啟動Spring就是為創(chuàng)建Bean對象做準備,所以我們先明白Spring到底是怎么去創(chuàng)建Bean的,也就是先弄明白Bean的生命周期。
Bean的生命周期就是指:在Spring中,一個Bean是如何生成的,如何銷毀的。
本節(jié)課的內(nèi)容,將會以下面這段代碼為入口講解:

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);
    }
}

主要用到的是如下Spring容器的構(gòu)造方法:

/**
 * 創(chuàng)建一個新的AnnotationConfigApplicationContext,掃描給定包中的組件,為這些組件注冊bean定義,并自動刷新上下文。
 * 參數(shù):
 * baseppackages——要掃描組件類的包
 */
public AnnotationConfigApplicationContext(String... basePackages) {
	this();
	scan(basePackages);
	refresh();
}

閱讀建議

  1. 看源碼,切記糾結(jié)細枝末節(jié),不然很容易陷進去。正常來說,看主要流程就好了
  2. 遇到不懂的,多看看類注釋或者方法注釋。Spring這種優(yōu)秀源碼,注釋真的非常到位
  3. 如果你是idea用戶,多用F11的書簽功能。
    • Ctrl + F11 選中文件 / 文件夾,使用助記符設定 / 取消書簽 (必備)
    • Shift + F11 彈出書簽顯示層 (必備)
    • Ctrl +1,2,3…9 定位到對應數(shù)值的書簽位置 (必備)

閱讀導航

系列上一篇文章:《【Spring專題】Spring之底層架構(gòu)核心概念解析》
系列下一篇文章:《【Spring專題】Spring之Bean的生命周期源碼解析——階段二(一)(IOC之實例化)》

課程內(nèi)容

一、生成BeanDefinition

1.1 簡單回顧

如果大家看過我前面的文章,應該會知道,BeanDefiniton的生成,是在生產(chǎn)Bean之前的【掃描】階段。如下圖所示:
【Spring專題】Spring之Bean的生命周期源碼解析——階段一(掃描生成BeanDefinition),tuling學院學習筆記,spring,java,后端
【Spring專題】Spring之Bean的生命周期源碼解析——階段一(掃描生成BeanDefinition),tuling學院學習筆記,spring,java,后端

不過咱也說過,這只是簡單實現(xiàn)而已,事實上,Spring實現(xiàn)這個掃描過程,涉及到了3個核心類,10個核心方法!

*1.2 概念回顧

在這個【掃描】過程中,涉及到了一些Spring底層設計的概念,我在上一個筆記里面有大概介紹過,不記得的同學記得回去翻一翻。
主要涉及的概念有:

  • BeanDefinition(設計圖紙):BeanDefinition表示Bean定義,BeanDefinition中存在很多屬性用來描述一個Bean的特征
  • ClassPathBeanDefinitionScanner(圖紙注冊器):用于注冊圖紙
  • BeanFacotory(Bean工廠):生產(chǎn)Bean。不過實際上,這里用到DefaultListableBeanFactory這個類的BeanDefinitionRegistry接口的注冊能力

文章鏈接:
《【Spring專題】Spring之底層架構(gòu)核心概念解析》

1.3 核心方法講解

我們在前面提到過,整個掃描流程會涉及到【3個核心類,10個核心方法】。下面,我們將會按照調(diào)用次序,依次講解各個方法。

二、方法講解

整個掃描流程的入口,是下面代碼中的scan()方法,而最終調(diào)用的是ClassPathBeanDefinitionScanner.scan()

/**
 * 創(chuàng)建一個新的AnnotationConfigApplicationContext,掃描給定包中的組件,為這些組件注冊bean定義,并自動刷新上下文。
 * 參數(shù):
 * 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

全路徑:org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan
方法注釋:在指定的基本包中執(zhí)行掃描

源碼如下:

	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);
	}

但其實真正干活的還不是這里,而是里面的doScan()

2.2 ClassPathBeanDefinitionScanner#doScan

方法調(diào)用鏈:由 2.1中的scan()調(diào)用進來
全路徑:org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan
方法注釋:在指定的基包中執(zhí)行掃描,返回已注冊的bean定義。此方法不注冊注釋配置處理程序,而是將此工作留給調(diào)用者。

源碼如下:

    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;
    }

方法解讀:這個代碼看似很多,其實整體來說比較簡單,分為4個步驟

  1. 先掃描classpath下所有的class,尋找候選的BeanDefinition。至于什么是候選的,后面會講解
  2. 遍歷找到的候選BeanDefinition,判斷是否需要設置BeanDefinition的一些屬性。比如默認屬性等
  3. 判斷是否要設置通用的注解BeanDefiniton屬性
  4. 判斷是否要注冊BeanDefinition,畢竟可能出現(xiàn)重復的BeanDefinition

2.3 ClassPathScanningCandidateComponentProvider#findCandidateComponents

方法調(diào)用鏈:由 2.2中的doScan()調(diào)用進來
全路徑:org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents
方法注釋:掃描類路徑上的候選Component。

源碼如下:

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

方法解讀:這里有一個if-else,但通常都是進入else。if里面的情況我們在業(yè)務開發(fā)中基本不會用到,這是一個為了加快掃描包的策略,通過使用索引思想。怎么實現(xiàn)呢?就是在resource下新建一個spring.components配置文件(K-V格式)。如下:

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

通過直接告訴Spring哪些是候選的Component,免除了掃描所有class文件篩選的過程,從而提升了效率。

2.4 ClassPathScanningCandidateComponentProvider#scanCandidateComponents

方法調(diào)用鏈:由 2.3中的findCandidateComponents()調(diào)用進來
全路徑:org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents
方法注釋:掃描類路徑上的候選Component。

源碼如下:

    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;
    }

方法解讀:代碼看著很長,實際上很簡單。主要分為4個步驟

  1. 讀取類元信息(從磁盤中讀取)
    • 首先,通過ResourcePatternResolver獲得指定包路徑下的所有.class文件(Spring源碼中將此文件包裝成了Resource對象)
    • 遍歷每個Resource對象
    • 利用MetadataReaderFactory解析Resource對象得到MetadataReader(在Spring源碼中MetadataReaderFactory具體的實現(xiàn)類為CachingMetadataReaderFactory,MetadataReader的具體實現(xiàn)類為SimpleMetadataReader)
  2. 判斷是否為候選組件,判斷核心為:是否需要過濾(第一個isCandidateComponent
    • 利用MetadataReader進行excludeFilters和includeFilters,以及條件注解@Conditional的篩選(條件注解并不能理解:某個類上是否存在@Conditional注解,如果存在則調(diào)用注解中所指定的類的match方法進行匹配,匹配成功則通過篩選,匹配失敗則pass掉。)
    • 篩選通過后,基于metadataReader生成ScannedGenericBeanDefinition
  3. 判斷是否為候選組件,判斷核心為:是否獨立、抽象類等。(第二個isCandidateComponent)(注意:他跟上面的isCandidateComponent不是同一個東西,屬于方法重載)
  4. 如果篩選通過,那么就表示掃描到了一個Bean,將ScannedGenericBeanDefinition加入結(jié)果集

MetadataReader表示類的元數(shù)據(jù)讀取器,主要包含了一個AnnotationMetadata,功能有

  • 獲取類的名字、
  • 獲取父類的名字
  • 獲取所實現(xiàn)的所有接口名
  • 獲取所有內(nèi)部類的名字
  • 判斷是不是抽象類
  • 判斷是不是接口
  • 判斷是不是一個注解
  • 獲取擁有某個注解的方法集合
  • 獲取類上添加的所有注解信息
  • 獲取類上添加的所有注解類型集合

2.5 ClassPathScanningCandidateComponentProvider#isCandidateComponent

方法調(diào)用鏈:由 2.4中的scanCandidateComponents()調(diào)用進來
全路徑:org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent
方法注釋:確定給定的類是否不匹配任何排除篩選器,而匹配至少一個包含篩選器。

源碼如下:

	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;
	}

方法解讀:這里只是校驗,當前類,是否在過濾器排除選項以內(nèi)。我估計很多朋友想到的都是@ComponentScan注解里面的includeFilterexcludeFilter屬性。只能說不完全正確。
因為,在我們啟動容器創(chuàng)建ClassPathBeanDefinitionScanner也會添加默認的過濾策略。如下所示:

/**
 * 注冊@Component的默認過濾器。
 * 這將隱式注冊所有帶有@Component元注釋的注釋,包括@Repository、@Service和@Controller構(gòu)造型注釋。
 * 還支持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

方法調(diào)用鏈:由 2.4中的scanCandidateComponents()調(diào)用進來
全路徑:org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent
方法注釋:確定給定的bean定義是否符合候選條件。默認實現(xiàn)檢查類是否不是接口,是否依賴于封閉類。

源碼如下:

/**
 *確定給定的bean定義是否符合候選條件。
 * 默認實現(xiàn)檢查類是否不是接口,是否依賴于封閉類。
 * 可以在子類中重寫。
 */
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
	AnnotationMetadata metadata = beanDefinition.getMetadata();
	return (metadata.isIndependent() && (metadata.isConcrete() ||
			(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}

方法解讀:這里有2個比較陌生的,那就是,什么是獨立類,以及Lookup注解。

  1. 獨立類,及不是普通內(nèi)部類(靜態(tài)內(nèi)部類是獨立類)
  2. Lookup注解,自己去百度使用方法

總結(jié)方法,判斷是候選Component的條件是:

  1. 首先是獨立類
  2. 要么是具體的子類,要么就是:有Lookup注解的抽象類

2.7 ClassPathBeanDefinitionScanner#postProcessBeanDefinition

方法調(diào)用鏈:由 2.2中的doScan()調(diào)用進來
全路徑:org.springframework.context.annotation.ClassPathBeanDefinitionScanner#postProcessBeanDefinition
方法注釋:掃描類路徑上的候選Component。

源碼如下:

/**
 * 除了通過掃描組件類檢索到的內(nèi)容之外,還可以對給定的bean定義應用進一步的設置。
 */
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
		beanDefinition.applyDefaults(this.beanDefinitionDefaults);
		if (this.autowireCandidatePatterns != null) {
			beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
		}
	}

方法解讀:這里主要還初始化了BeanDefinition一些默認屬性beanDefinition.applyDefaults(this.beanDefinitionDefaults);
源碼如下:

	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

方法調(diào)用鏈:由 2.2中的doScan()調(diào)用進來
全路徑:org.springframework.context.annotation.AnnotationConfigUtils#processCommonDefinitionAnnotations
方法注釋:根據(jù)注解累類元信息,設置BeanDefinition注解相關(guān)屬性。

源碼如下:

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"));
		}
	}

沒啥好解讀的,很簡單的判斷

2.9 ClassPathBeanDefinitionScanner#checkCandidate

方法調(diào)用鏈:由 2.2中的doScan()調(diào)用進來
全路徑:org.springframework.context.annotation.ClassPathBeanDefinitionScanner#checkCandidate
方法注釋:檢查給定的候選bean名稱,確定是否需要注冊相應的bean定義,或者是否與現(xiàn)有定義沖突。

源碼如下:

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() + "]");
	}

方法解讀:這里正常來說,要么返回true,要么就是重復定義相同名字的Bean然后拋出異常。直接返回false,通常是存在父子容器的情況下。

2.10 DefaultListableBeanFactory#registerBeanDefinition

方法調(diào)用鏈:由 2.2中的doScan()調(diào)用進來
全路徑:org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
方法注釋:用這個注冊中心注冊一個新的bean定義。必須支持RootBeanDefinition和ChildBeanDefinition。

源碼如下:

@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();
		}
	}

方法解讀:這個就稍微復雜一點了,我也沒細看。而且,這個隱藏的挺深的,雖然是從doScan()里面進調(diào)用的,但是本質(zhì)上是調(diào)用AnnotationConfigApplicationContext繼承自GenericApplicationContextregisterBeanDefinition方法

三、掃描邏輯流程圖

【Spring專題】Spring之Bean的生命周期源碼解析——階段一(掃描生成BeanDefinition),tuling學院學習筆記,spring,java,后端

  1. 首先通過ResourcePatternResolver獲得指定包路徑下所有的class文件,并且包裝成Resource
  2. 遍歷每Resource對象
  3. 利用ASM技術(shù)解析每一個文件,得到對應的類元信息,封裝成MetadataReader
  4. 用excludeFilter跟includeFilter,以及@Conditionnal注解進行篩選過濾
  5. 篩選通過后,基于metadataReader生成ScannedGenericBeanDefinition
  6. 再基于metadataReader判斷是不是對應的類是不是獨立類,接口或抽象類
  7. 如果篩選通過,那么就表示掃描到了一個Bean,將ScannedGenericBeanDefinition加入結(jié)果集

四、合并BeanDefinition

到了后面的IOC過程,生成Bean的時候,我們會看到很多這樣的調(diào)用:

RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);

這個就是所謂的,獲取合并BeanDefinition。什么是合并BeanDefinition?其實就是父子BeanDefinition。跟Java里面的繼承是一樣一樣的。但是,這個通常發(fā)生在xml配置bean里面,如下:

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

但是這么定義的情況下,child就是原型Bean了。因為child的父BeanDefinition是parent,所以會繼承parent上所定義的scope屬性。而在根據(jù)child來生成Bean對象之前,需要進行BeanDefinition的合并,得到完整的child的BeanDefinition。文章來源地址http://www.zghlxwxcb.cn/news/detail-647212.html

學習總結(jié)

  1. 學習了Spring源碼流程中的【掃描】底層原理

到了這里,關(guān)于【Spring專題】Spring之Bean的生命周期源碼解析——階段一(掃描生成BeanDefinition)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務器費用

相關(guān)文章

  • 【Spring】Spring之Bean生命周期源碼解析

    什么是bean的生命周期 是指bean在spring中是如何生成,如何銷毀的; spring創(chuàng)建對象的過程,就是IOC(控制反轉(zhuǎn))的過程; JFR Java Flight Record,java飛行記錄,類似于飛機的黑匣子,是JVM內(nèi)置的基于事件的JDK監(jiān)控記錄框架,主要用于問題定位和持續(xù)監(jiān)控; 入口代碼: Spring啟動的時

    2024年02月15日
    瀏覽(23)
  • 【框架源碼】Spring源碼解析之Bean生命周期流程

    【框架源碼】Spring源碼解析之Bean生命周期流程

    觀看本文前,我們先思考一個問題,什么是Spring的bean的生命周期?這也是我們在面試的時候,面試官常問的一個問題。 在沒有Spring之前,我們創(chuàng)建對象的時候,采用new的方式,當對象不在被使用的時候,由Java的垃圾回收機制回收。 而 Spring 中的對象是 bean,bean 和普通的 J

    2024年02月09日
    瀏覽(21)
  • 【深入Spring源碼解析:解密Bean的生命周期】

    Spring是Java企業(yè)級應用開發(fā)領(lǐng)域的一顆明星,它提供了很多方便開發(fā)人員的工具和思想。在分布式系統(tǒng)中,Spring的分布式遠程協(xié)作方案,比如REST、Web服務以及消息傳遞等,也是不可或缺的。 你知道嗎?在我們使用Spring時,容器中存放的所有對象,在Spring啟動的時候就完成了實

    2024年02月05日
    瀏覽(28)
  • Spring的Bean生命周期有哪些階段?

    Spring的Bean生命周期分為四個階段:實例化、屬性賦值、初始化和銷毀。 實例化:Spring容器負責創(chuàng)建Bean的實例,可以通過構(gòu)造方法或者無參構(gòu)造方法進行實例化。 屬性賦值:Spring容器通過屬性注入的方式為Bean的屬性賦值,可以通過setter方法或者無參構(gòu)造方法進行屬性賦值。

    2024年02月09日
    瀏覽(28)
  • 深入理解 Java Bean 的生命周期及各個階段解析

    深入理解 Java Bean 的生命周期及各個階段解析

    Java Bean是Java編程中經(jīng)常使用的重要概念,它是可重用、可移植、可序列化的組件。在Java開發(fā)中,我們常常會遇到Bean對象,但是對于Bean的生命周期和各個階段可能并不完全了解。本文將深入探討Java Bean的生命周期,逐步解析Bean對象從創(chuàng)建到銷毀的各個重要階段。 Java Bean是一

    2024年02月14日
    瀏覽(21)
  • 【Spring專題】Bean的生命周期流程圖

    【Spring專題】Bean的生命周期流程圖

    我向來不主張【通過源碼】理解業(yè)務,因為每個人的能力有限,甚至可能會因為閱讀錯誤導致出現(xiàn)理解上的偏差,所以我決定,還是先幫大家【開天眼】,先整體看看流程圖,好知道,Spring在寫源碼的過程中到底干了啥事情。 對于【一、之前推測的簡單流程圖】大家可以不看

    2024年02月13日
    瀏覽(34)
  • Spring源碼:Bean生命周期(五)

    在上一篇文章中,我們深入探討了 Spring 框架中 Bean 的實例化過程,該過程包括從 Bean 定義中加載當前類、尋找所有實現(xiàn)了 InstantiationAwareBeanPostProcessor 接口的類并調(diào)用實例化前的方法、進行實例化、調(diào)用 applyMergedBeanDefinitionPostProcessors 方法等多個步驟,最終生成了一個真正的

    2024年02月04日
    瀏覽(21)
  • Spring源碼:Bean生命周期(終章)

    本系列前面講解了Spring的bean定義、bean實例化、bean初始化等生命周期。這些步驟使我們能夠了解bean從創(chuàng)建到準備好使用所經(jīng)歷的過程。但是,除了這些步驟,bean的銷毀也是非常重要的一步。在本系列的最后,我們將深入探討bean的銷毀過程,包括在什么情況下會發(fā)生銷毀、銷

    2024年02月06日
    瀏覽(35)
  • Spring源碼:Bean生命周期(三)

    在之前的文章中,我們已經(jīng)對 bean 的準備工作進行了講解,包括 bean 定義和 FactoryBean 判斷等。在這個基礎上,我們可以更加深入地理解 getBean 方法的實現(xiàn)邏輯,并在后續(xù)的學習中更好地掌握 createBean 方法的實現(xiàn)細節(jié)。 講解getBean方法之前,我們先來看看他有幾種常見的用法:

    2024年02月02日
    瀏覽(23)
  • Spring源碼:bean的生命周期(一)

    Spring源碼:bean的生命周期(一)

    本節(jié)將正式介紹Spring源碼細節(jié),將講解Bean生命周期。請注意,雖然我們不希望過于繁瑣地理解Spring源碼,但也不要認為Spring源碼很簡單。在本節(jié)中,我們將主要講解Spring 5.3.10版本的源代碼。如果您看到的代碼與我講解的不同,也沒有關(guān)系,因為其中的原理和業(yè)務邏輯基本相

    2024年02月02日
    瀏覽(23)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包