本文主要讲述Spring是如何解析“context:component-scan”元素,扫描加载目录下的BeanDefinition。
解析内容
1、解析的元素如下:
<!-- 注解模式:配置bean扫描路径(注:自动包含子路径) -->
<context:component-scan base-package="com.learnsf.main,com.learnsf.service"/>
注:该元素解析过程中,会自动处理“context:annotation-config/”元素要解析的内容。
2、只扫描加载目录下的BeanDefinition,不对注解进行解析。在AbstractApplicationContext.invokeBeanFactoryPostProcessors统一解析。
解析
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);
// 分解成包数组
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属性默认值,并设置 autowireCandidate 属性
postProcessBeanDefinition(abstractBeanDefinition, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition annotatedBeanDefinition) {
// 对 AnnotatedBeanDefinition 类型的 BeanDefinition 进一步处理:对通用注解的解析处理,通用注解包括 @Lazy、@Primary、@DependsOn、@Role、@Description。例如:如果当前类被@Lazy修饰,则会获取@Lazy 的value 值并保存到 BeanDefinition#lazyInit 属性中。
AnnotationConfigUtils.processCommonDefinitionAnnotations(annotatedBeanDefinition);
}
// 检查给定候选bean的beanName,确定相应的bean定义是否需要注册或与现有定义冲突
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注释的类的模式解析结果。当Spring应用上下文进行组件扫描时,META-INF/spring.components会被org.springframework.context.index.CandidateComponentsIndexLoader读取并加载,转换为CandidateComponentsIndex对象,此时组件扫描会读取CandidateComponentsIndex,而不进行实际扫描,从而提高组件扫描效率,减少应用启动时间。如果使用该功能,需要引入如下依赖:文章来源:https://www.toymoban.com/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)
该方法是从指定的包路径获取到字节码文件,筛选出可能注入到Spring容器的Bean生成对应的ScannedGenericBeanDefinition文章来源地址https://www.toymoban.com/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;
// 扫描路径下的资源(字节码文件)
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
// 遍历所有资源(字节码文件),挑选有注解的字节码文件生成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(包含文件信息和对应类注解信息)
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);
// 构建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标签的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));
}
}
// 触发组件注册事件
readerContext.fireComponentRegistered(compositeDef);
}
到了这里,关于一起学SF框架系列5.8-spring-Beans-Bean注解解析3-解析配置component-scan的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!