SpringIoC源碼如何解析包掃描

Spring IoC 源碼如何解析包掃描,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

成都創(chuàng)新互聯(lián)于2013年開始,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務公司,擁有項目成都網(wǎng)站制作、成都網(wǎng)站設(shè)計網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元安平做網(wǎng)站,已為上家服務,為安平各地企業(yè)和個人服務,聯(lián)系電話:18980820575

我們通過AnnotationConfigApplicationContext類傳入一個包路徑啟動Spring之后,會首先初始化包掃描的過濾規(guī)則。那我們今天就來看下包掃描的具體過程。

還是先看下面的代碼:
AnnotationConfigApplicationContext類

//該構(gòu)造函數(shù)會自動掃描以給定的包及其子包下的所有類,并自動識別所有的Spring Bean,將其注冊到容器中
	public AnnotationConfigApplicationContext(String... basePackages) {
		//初始化
		this();
		//掃描包、注冊bean
		scan(basePackages);
		refresh();
	}

上文我們分析了this()方法,會去初始化AnnotatedBeanDefinitionReader讀取器和ClassPathBeanDefinitionScanner掃描器,并初始化掃描過濾規(guī)則。
接下來我們看一下scan(basePackages)方法:
一直跟蹤下去,發(fā)現(xiàn)調(diào)用了ClassPathBeanDefinitionScanner類中的scan()方法

//調(diào)用類路徑Bean定義掃描器入口方法
	public int scan(String... basePackages) {
		//獲取容器中已經(jīng)注冊的Bean個數(shù)
		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

		//啟動掃描器掃描給定包
		doScan(basePackages);

		// Register annotation config processors, if necessary.
		//注冊注解配置(Annotation config)處理器
		if (this.includeAnnotationConfig) {
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
		}

		//返回注冊的Bean個數(shù)
		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
	}

可以看到主要是doScan(basePackages)方法實現(xiàn)了掃描的邏輯,我們繼續(xù)跟蹤進去看下

//類路徑Bean定義掃描器掃描給定包及其子包
	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		//創(chuàng)建一個集合,存放掃描到Bean定義的封裝類
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		//遍歷掃描所有給定的包
		for (String basePackage : basePackages) {
			//調(diào)用父類ClassPathScanningCandidateComponentProvider的方法
			//掃描給定類路徑,獲取符合條件的Bean定義
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			//遍歷掃描到的Bean
			for (BeanDefinition candidate : candidates) {
				//獲取@Scope注解的值,即獲取Bean的作用域
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				//為Bean設(shè)置作用域
				candidate.setScope(scopeMetadata.getScopeName());
				//為Bean生成名稱
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				//如果掃描到的Bean不是Spring的注解Bean,則為Bean設(shè)置默認值,
				//設(shè)置Bean的自動依賴注入裝配屬性等
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				//如果掃描到的Bean是Spring的注解Bean,則處理其通用的Spring注解
				if (candidate instanceof AnnotatedBeanDefinition) {
					//處理注解Bean中通用的注解,在分析注解Bean定義類讀取器時已經(jīng)分析過
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				//根據(jù)Bean名稱檢查指定的Bean是否需要在容器中注冊,或者在容器中沖突
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					//根據(jù)注解中配置的作用域,為Bean應用相應的代理模式
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					//向容器注冊掃描到的Bean
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

這一大段代碼基本上就是spring掃描識別注解,并注冊Bean到IOC容器中的代碼。
在第10行有一個findCandidateComponents(basePackage)方法,這個方法里就是具體的掃描邏輯。
繼續(xù)跟蹤:
ClassPathScanningCandidateComponentProvider類

//掃描給定類路徑的包
	public Set<BeanDefinition> findCandidateComponents(String basePackage) {
		//spring5.0開始 索引 開啟的話生成文件META-INF/spring.components 后面加載直接從本地文件讀取(一般不建議開啟 spring.index.ignore=true)
		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
		}
		else {
			return scanCandidateComponents(basePackage);
		}
	}

這里有一個if判斷,我們默認走的是else里的分支,即scanCandidateComponents(basePackage)方法。

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			//補全掃描路徑,掃描所有.class文件 classpath*:com/mydemo/**/*.class
			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);
				}
				if (resource.isReadable()) {
					try {
						//通過ASM獲取class元數(shù)據(jù),并封裝在MetadataReader元數(shù)據(jù)讀取器中
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
						//判斷該類是否符合@CompoentScan的過濾規(guī)則
						//過濾匹配排除excludeFilters排除過濾器(可以沒有),包含includeFilter中的包含過濾器(至少包含一個)。
						if (isCandidateComponent(metadataReader)) {
							//把元數(shù)據(jù)轉(zhuǎn)化為 BeanDefinition
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							sbd.setResource(resource);
							sbd.setSource(resource);
							//判斷是否是合格的bean定義
							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 {
							//不符@CompoentScan過濾規(guī)則
							if (traceEnabled) {
								logger.trace("Ignored because not matching any filter: " + resource);
							}
						}
					}
					catch (Throwable ex) {
						throw new BeanDefinitionStoreException(
								"Failed to read candidate component class: " + resource, ex);
					}
				}
				else {
					if (traceEnabled) {
						logger.trace("Ignored because not readable: " + resource);
					}
				}
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
		}
		return candidates;
	}

這里就是主要的掃描邏輯,代碼中的注釋已經(jīng)說的很清楚了。
主要過程:

  • 根據(jù)包路徑,掃描所有.class文件

  • 根據(jù)包路徑,生成.class對應的Resource對象

  • 通過ASM獲取class元數(shù)據(jù),并封裝在MetadataReader元數(shù)據(jù)讀取器中

  • 判斷該類是否符合過濾規(guī)則

  • 判斷該類是否為獨立的類、具體的類

  • 加入到集合中

我們來詳細看下過濾的方法 isCandidateComponent(metadataReader)

//判斷元信息讀取器讀取的類是否符合容器定義的注解過濾規(guī)則
	//@CompoentScan的過濾規(guī)則支持5種 (注解、類、正則、aop、自定義)
	protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		//如果讀取的類的注解在排除注解過濾規(guī)則中,返回false
		for (TypeFilter tf : this.excludeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return false;
			}
		}
		//如果讀取的類的注解在包含的注解的過濾規(guī)則中,則返回ture
		for (TypeFilter tf : this.includeFilters) {
			//判斷當前類的注解是否match規(guī)則
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				//是否有@Conditional注解,進行相關(guān)處理
				return isConditionMatch(metadataReader);
			}
		}
		//如果讀取的類的注解既不在排除規(guī)則,也不在包含規(guī)則中,則返回false
		return false;
	}

接著跟蹤 tf.match()方法
AbstractTypeHierarchyTraversingFilter類

public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
			throws IOException {

		// This method optimizes avoiding unnecessary creation of ClassReaders
		// as well as visiting over those readers.
		//檢查當前類的注解是否符合規(guī)律規(guī)則
		if (matchSelf(metadataReader)) {
			return true;
		}
		//check 類名是否符合規(guī)則
		ClassMetadata metadata = metadataReader.getClassMetadata();
		if (matchClassName(metadata.getClassName())) {
			return true;
		}

		//如果有繼承父類
		if (this.considerInherited) {
			String superClassName = metadata.getSuperClassName();
			if (superClassName != null) {
				// Optimization to avoid creating ClassReader for super class.
				Boolean superClassMatch = matchSuperClass(superClassName);
				if (superClassMatch != null) {
					if (superClassMatch.booleanValue()) {
						return true;
					}
				}
				else {
					// Need to read super class to determine a match...
					try {
						if (match(metadata.getSuperClassName(), metadataReaderFactory)) {
							return true;
						}
					}
					catch (IOException ex) {
						logger.debug("Could not read super class [" + metadata.getSuperClassName() +
								"] of type-filtered class [" + metadata.getClassName() + "]");
					}
 				}
			}
		}

		//如果有實現(xiàn)接口
		if (this.considerInterfaces) {
			for (String ifc : metadata.getInterfaceNames()) {
				// Optimization to avoid creating ClassReader for super class
				Boolean interfaceMatch = matchInterface(ifc);
				if (interfaceMatch != null) {
					if (interfaceMatch.booleanValue()) {
						return true;
					}
				}
				else {
					// Need to read interface to determine a match...
					try {
						if (match(ifc, metadataReaderFactory)) {
							return true;
						}
					}
					catch (IOException ex) {
						logger.debug("Could not read interface [" + ifc + "] for type-filtered class [" +
								metadata.getClassName() + "]");
					}
				}
			}
		}

		return false;
	}

這里面最主要的是 matchSelf(metadataReader) 方法
AnnotationTypeFilter類

protected boolean matchSelf(MetadataReader metadataReader) {
		//獲取注解元數(shù)據(jù)
		AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
		//check 注解及其派生注解中是否包含@Component
		//獲取當前類的注解 metadata.hasAnnotation    @Controller
		//獲取當前類的注解及其派生注解 metadata.hasAnnotation   @Controller包含的@Component\@Documented等等
		return metadata.hasAnnotation(this.annotationType.getName()) ||
				(this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
	}

在這段代碼代碼中,可以解決我們之前的疑惑“Spring是怎么發(fā)現(xiàn)@Configuration、@Controller、@Service這些注解修飾的類的?
原來@Configuration、@Controller、@Service這些注解其實都是@Component的派生注解,我們看這些注解的代碼會發(fā)現(xiàn),都有@Component注解修飾。而spring通過metadata.hasMetaAnnotation()方法獲取到這些注解包含@Component,所以都可以掃描到。如下:

Spring IoC 源碼如何解析包掃描

然后我們再看回 scanCandidateComponents(basePackage)方法,接下來有一個 isCandidateComponent(sbd)方法,如下:

//是否是獨立的類、具體的類
	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		AnnotationMetadata metadata = beanDefinition.getMetadata();
		return (metadata.isIndependent() && (metadata.isConcrete() ||
				(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
	}

這個方法的作用是,判斷該類是否為

  • 頂層的類(沒有父類或靜態(tài)內(nèi)部類)

  • 具體的類(不是抽象類或接口)

至此,ClassPathBeanDefinitionScanner類中的doScan(basePackages)方法中的findCandidateComponents(basePackage)方法已經(jīng)結(jié)束了,即我們的包掃描也結(jié)束了,已經(jīng)把掃描到的類存入到了集合中,結(jié)下來就是解析注冊Bean的過程了。

總結(jié)
通過這篇文章,我們可以回答之前的一些問題了:

  • Spring是怎么發(fā)現(xiàn)@Bean、@Controller、@Service這些注解修飾的類的?
    通過 matchSelf(metadataReader)方法,判斷這些注解中是否包含@Component

  • @CompoentScan注解是怎么起作用的?
    通過 isCandidateComponent(metadataReader)方法過濾

關(guān)于Spring IoC 源碼如何解析包掃描問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識。

文章名稱:SpringIoC源碼如何解析包掃描
URL網(wǎng)址:http://muchs.cn/article16/gdesgg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站營銷、關(guān)鍵詞優(yōu)化、企業(yè)建站、ChatGPT、微信小程序、網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

綿陽服務器托管