本文主要講述Spring是如何解析“context:component-scan”元素,掃描加載目錄下的BeanDefinition。
解析內(nèi)容
1、解析的元素如下:
<!-- 注解模式:配置bean掃描路徑(注:自動包含子路徑) -->
<context:component-scan base-package="com.learnsf.main,com.learnsf.service"/>
注:該元素解析過程中,會自動處理“context:annotation-config/”元素要解析的內(nèi)容。
2、只掃描加載目錄下的BeanDefinition,不對注解進行解析。在AbstractApplicationContext.invokeBeanFactoryPostProcessors統(tǒng)一解析。
解析
ComponentScanBeanDefinitionParser.parse(Element element, ParserContext parserContext)
解析元素“context:component-scan”。
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 獲取屬性“base-package”(多個包路徑)
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
// 對包路徑中占位符進行替換處理
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
// 分解成包數(shù)組
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
// 生成BeanDefinition掃描器
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
// 掃描生成beanDefinition
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
// 注冊BeanDefinition
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
ClassPathBeanDefinitionScanner.doScan(String… basePackages)
在包里掃描BeanDefinition。
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) {
// 解析Bean作用域
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
// 生成beanName
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition abstractBeanDefinition) {
// 對 AbstractBeanDefinition類型的BeanDefinition 進一步處理:賦值BeanDefinition屬性默認值,并設(shè)置 autowireCandidate 屬性
postProcessBeanDefinition(abstractBeanDefinition, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition annotatedBeanDefinition) {
// 對 AnnotatedBeanDefinition 類型的 BeanDefinition 進一步處理:對通用注解的解析處理,通用注解包括 @Lazy、@Primary、@DependsOn、@Role、@Description。例如:如果當(dāng)前類被@Lazy修飾,則會獲取@Lazy 的value 值并保存到 BeanDefinition#lazyInit 屬性中。
AnnotationConfigUtils.processCommonDefinitionAnnotations(annotatedBeanDefinition);
}
// 檢查給定候選bean的beanName,確定相應(yīng)的bean定義是否需要注冊或與現(xiàn)有定義沖突
if (checkCandidate(beanName, candidate)) {
// 封裝候選BeanDefinition為BeanDefinitionHolder
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
// 對 BeanDefinitionHolder 填充代理信息
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
// 加入到返回集合
beanDefinitions.add(definitionHolder);
// 注冊BeanDefinitionHolder 到bean工廠容器中
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
ClassPathScanningCandidateComponentProvider.findCandidateComponents(String basePackage)
ClassPathScanningCandidateComponentProvider是ClassPathBeanDefinitionScanner父類。
獲取注解的Bean的BeanDefinition。
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
// @Indexed 注解的處理 注1
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
// 非@Indexed 注解的處理
return scanCandidateComponents(basePackage);
}
}
注1:@Indexed注解Spring在5.0版本引入的,主要解決啟動時注解模式解析太長的問題。處理方式是在項目編譯打包時,會自動生成META-INF/spring.components文件,文件包含被@Indexed注釋的類的模式解析結(jié)果。當(dāng)Spring應(yīng)用上下文進行組件掃描時,META-INF/spring.components會被org.springframework.context.index.CandidateComponentsIndexLoader讀取并加載,轉(zhuǎn)換為CandidateComponentsIndex對象,此時組件掃描會讀取CandidateComponentsIndex,而不進行實際掃描,從而提高組件掃描效率,減少應(yīng)用啟動時間。如果使用該功能,需要引入如下依賴:文章來源:http://www.zghlxwxcb.cn/news/detail-600883.html
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<version>${spring.version}</version>
<optional>true</optional>
</dependency>
ClassPathScanningCandidateComponentProvider.scanCandidateComponents(String basePackage)
該方法是從指定的包路徑獲取到字節(jié)碼文件,篩選出可能注入到Spring容器的Bean生成對應(yīng)的ScannedGenericBeanDefinition文章來源地址http://www.zghlxwxcb.cn/news/detail-600883.html
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
// // 形成完整包路徑
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
// 掃描路徑下的資源(字節(jié)碼文件)
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
// 遍歷所有資源(字節(jié)碼文件),挑選有注解的字節(jié)碼文件生成BeanDefinition
for (Resource resource : resources) {
String filename = resource.getFilename();
if (filename != null && filename.contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
// Ignore CGLIB-generated classes in the classpath
continue;
}
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
try {
// 獲得資源的MetadataReader(包含文件信息和對應(yīng)類注解信息)
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
// 校驗是否是候選組件,條件是:包含在include-filters(掃描時需要實例化的類,默認都包含)且 @Conditional注解中不跳過的類(默認都不跳過)
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
// 校驗是否是候選組件:bean是獨立且具體的類 或者 是抽象類但被@Lookup注解修飾
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;
}
ComponentScanBeanDefinitionParser.registerComponents( XmlReaderContext readerContext, Set beanDefinitions, Element element)
protected void registerComponents(
XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {
Object source = readerContext.extractSource(element);
// 構(gòu)建CompositeComponentDefinition
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);
// 將所有BeanDefinition添加到compositeDef的nestedComponents屬性中
for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {
compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));
}
// Register annotation config processors, if necessary.
// 處理“annotation-config”:假定annotation-config是存在,這意味著配置了“context:component-scan”,則不需要再配置“context:annotation-config”
boolean annotationConfig = true;
if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
// 獲取component-scan標(biāo)簽的annotation-config屬性值(默認為true)
annotationConfig = Boolean.parseBoolean(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
}
if (annotationConfig) {
// 獲取所有處理注解類的BeanPostProcessors(BeanPostProcessor本身也是bean)
Set<BeanDefinitionHolder> processorDefinitions =
AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
// 將所有BeanPostProcessors的BeanDefinition添加到compositeDef的nestedComponents屬性中
for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
}
}
// 觸發(fā)組件注冊事件
readerContext.fireComponentRegistered(compositeDef);
}
到了這里,關(guān)于一起學(xué)SF框架系列5.8-spring-Beans-Bean注解解析3-解析配置component-scan的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!