Spring源码- context:component-scan base-package标签的作用源码解析

news2024/11/25 2:59:13

1.扫描包路径下所有的类加载解析成bean定义信息
在这里插入图片描述
ClassPathBeanDefinitionScanner .doScan方法调用路径

doScan:276, ClassPathBeanDefinitionScanner (org.springframework.context.annotation)
parse:95, ComponentScanBeanDefinitionParser (org.springframework.context.annotation)
parse:76, NamespaceHandlerSupport (org.springframework.beans.factory.xml)
parseCustomElement:1475, BeanDefinitionParserDelegate (org.springframework.beans.factory.xml)
parseCustomElement:1452, BeanDefinitionParserDelegate (org.springframework.beans.factory.xml)
parseBeanDefinitions:181, DefaultBeanDefinitionDocumentReader (org.springframework.beans.factory.xml)
doRegisterBeanDefinitions:150, DefaultBeanDefinitionDocumentReader (org.springframework.beans.factory.xml)
registerBeanDefinitions:97, DefaultBeanDefinitionDocumentReader (org.springframework.beans.factory.xml)
registerBeanDefinitions:526, XmlBeanDefinitionReader (org.springframework.beans.factory.xml)
doLoadBeanDefinitions:397, XmlBeanDefinitionReader (org.springframework.beans.factory.xml)
loadBeanDefinitions:341, XmlBeanDefinitionReader (org.springframework.beans.factory.xml)
loadBeanDefinitions:310, XmlBeanDefinitionReader (org.springframework.beans.factory.xml)
loadBeanDefinitions:190, AbstractBeanDefinitionReader (org.springframework.beans.factory.support)
loadBeanDefinitions:234, AbstractBeanDefinitionReader (org.springframework.beans.factory.support)
loadBeanDefinitions:197, AbstractBeanDefinitionReader (org.springframework.beans.factory.support)
loadBeanDefinitions:267, AbstractBeanDefinitionReader (org.springframework.beans.factory.support)
loadBeanDefinitions:150, AbstractXmlApplicationContext (org.springframework.context.support)
loadBeanDefinitions:102, AbstractXmlApplicationContext (org.springframework.context.support)
refreshBeanFactory:138, AbstractRefreshableApplicationContext (org.springframework.context.support)
obtainFreshBeanFactory:718, AbstractApplicationContext (org.springframework.context.support)
refresh:577, AbstractApplicationContext (org.springframework.context.support)
<init>:150, ClassPathXmlApplicationContext (org.springframework.context.support)
<init>:98, ClassPathXmlApplicationContext (org.springframework.context.support)
<init>:15, MyClassPathXmlApplicationContext (com.mashibing)
main:84, Test (com.mashibing)

parse:95, ComponentScanBeanDefinitionParser (org.springframework.context.annotation)

解析节点使用scanner在执行的basePackages包中执行扫描,返回已注册的bean定义

	@Override
	@Nullable
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		// 获取<context:component-scan>节点的base-package属性值
		String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);//base-package
		// 解析占位符
		basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
		// 解析base-package(允许通过,;\t\n中的任一符号填写多个)
		String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

		// Actually scan for bean definitions and register them.
		// 构建和配置ClassPathBeanDefinitionScanner
		ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);//类路径Bean定义扫描器
		// 使用scanner在执行的basePackages包中执行扫描,返回已注册的bean定义
		Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
		// 组件注册(包括注册一些内部的注解后置处理器,触发注册事件)
		registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

		return null;
	}

doScan:276, ClassPathBeanDefinitionScanner (org.springframework.context.annotation)

扫描开始

	/**
	 * Perform a scan within the specified base packages, 在指定的基本包内执行扫描,
	 * returning the registered bean definitions. 返回已注册的bean定义。
	 * <p>This method does <i>not</i> register an annotation config processor 这个方法不注册注释配置处理器
	 * but rather leaves this up to the caller. 而是把这个问题留给调用者。
	 * @param basePackages the packages to check for annotated classes 要检查带注释的类的包
	 * @return set of beans registered if any for tooling registration purposes (never {@code null}) 为了工具注册目的而注册的bean集(从不{@code null})
	 */
	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		// 遍历basePackages
		for (String basePackage : basePackages) {
			// 扫描basePackage,将符合要求的bean定义全部找出来
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			// 遍历所有候选的bean定义
			for (BeanDefinition candidate : candidates) {
				// 解析@Scope注解,包括scopeName和proxyMode
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				// 使用beanName生成器来生成beanName
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					// 处理beanDefinition对象,例如,此bean是否可以自动装配到其他bean中
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					// 处理定义在目标类上的通用注解,包括@Lazy,@Primary,@DependsOn,@Role,@Description
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				// 检查beanName是否已经注册过,如果注册过,检查是否兼容
				if (checkCandidate(beanName, candidate)) {
					// 将当前遍历bean的bean定义和beanName封装成BeanDefinitionHolder
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					// 根据proxyMode的值,选择是否创建作用域代理
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					// 注册beanDefinition
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

// 扫描basePackage,将符合要求的bean定义全部找出来
Set candidates = findCandidateComponents(basePackage);

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

	private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {//组装扫描路径(组装完成后是这种格式:classpath*:org/springframework/learn/demo01/**/*.class)
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);//根据路径获取资源对象,即扫描出该路径下的的所有class文件,得到 Resource
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
			for (Resource resource : resources) {
				if (traceEnabled) {
					logger.trace("Scanning " + resource);
				}
				if (resource.isReadable()) {
					try {//根据资源对象获取资源对象的MetadataReader
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
						if (isCandidateComponent(metadataReader)) {// 1. 是否需要初始化为spring bean,即是否有 @Component、@Service等注解//2. 查看配置类是否有@Conditional一系列的注解,然后是否满足注册Bean的条件
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);// 将 metadataReader 转换为 ScannedGenericBeanDefinition,这也是BeanDefinition家族中的一员
							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 (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;
	}

判断是否可以生成bean定义信息

	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;
	}
	/**
	 * Determine whether the given class is a candidate component based on any 确定给定的类是否是基于任何的候选组件
	 * {@code @Conditional} annotations.{@code @Conditional}注释。
	 * @param metadataReader the ASM ClassReader for the class
	 * @return whether the class qualifies as a candidate component
	 */
	private boolean isConditionMatch(MetadataReader metadataReader) {
		if (this.conditionEvaluator == null) {
			this.conditionEvaluator =
					new ConditionEvaluator(getRegistry(), this.environment, this.resourcePatternResolver);
		}
		return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());
	}
    /**
     * Determine if an item should be skipped based on {@code @Conditional} annotations.
     * The {@link ConfigurationPhase} will be deduced from the type of item (i.e. a
     * {@code @Configuration} class will be {@link ConfigurationPhase#PARSE_CONFIGURATION})
     *
     * @param metadata the meta data
     * @return if the item should be skipped
     */
    public boolean shouldSkip(AnnotatedTypeMetadata metadata) {
        return shouldSkip(metadata, null);
    }
 /**
     * 基于@Conditional标签判断该对象是否要跳过
     * <p>
     * Determine if an item should be skipped based on {@code @Conditional} annotations.
     *
     * @param metadata the meta data
     * @param phase    the phase of the call
     * @return if the item should be skipped
     */
    public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
        // metadata为空或者配置类中不存在@Conditional标签
        if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
            return false;
        }

        // 采用递归的方式进行判断,第一次执行的时候phase为空,向下执行 // 判断传入的 phase
        if (phase == null) {
            // 下面的逻辑判断中,需要进入ConfigurationClassUtils.isConfigurationCandidate方法,主要的逻辑如下:
            // 1、metadata是AnnotationMetadata类的一个实例
            // 2、检查bean中是否使用@Configuration注解
            // 3、检查bean不是一个接口
            // 4、检查bean中是否包含@Component @ComponentScan @Import @ImportResource中任意一个
            // 5、检查bean中是否有@Bean注解
            // 只要满足其中1,2或者1,3或者1,4或者1,5就会继续递归
            if (metadata instanceof AnnotationMetadata &&
                    ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
                return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
            }
            return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
        }
        // 实例化 condition,其放入 conditions 中
        List<Condition> conditions = new ArrayList<>();
        for (String[] conditionClasses : getConditionClasses(metadata)) {// 1. getConditionClasses(metadata):获取 @Conditional 中指定的判断类
            for (String conditionClass : conditionClasses) {
                // 获取到@Conditional注解后面的value数组  // 2. 实例化操作(用到的还是反射),统一放到 conditions 中
                Condition condition = getCondition(conditionClass, this.context.getClassLoader());
                conditions.add(condition);
            }
        }
        // 对相关的条件进行排序操作
        AnnotationAwareOrderComparator.sort(conditions);// 3. 排序上面得到的 condition 实例

        for (Condition condition : conditions) {
            ConfigurationPhase requiredPhase = null;
            if (condition instanceof ConfigurationCondition) {
                requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
            }
            // requiredPhase只可能是空或者是ConfigurationCondition的一个实例对象
            if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {

                //此逻辑为:1.requiredPhase不是ConfigurationCondition的实例
                //2.phase==requiredPhase,从上述的递归可知:phase可为ConfigurationPhase.PARSE_CONFIGURATION或者ConfigurationPhase.REGISTER_BEAN
                //3.condition.matches(this.context, metadata)返回false
                //如果1、2或者1、3成立,则在此函数的上层将阻断bean注入Spring容器
                return true;// 4. 调用 Condition#matches 方法进行判断
            }
        }

        return false;
    }

ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata))

/**
	 * 检查给定的元数据,以查找给定的候选配置类是否被指定的注解标注
	 *
	 * Check the given metadata for a configuration class candidate
	 * (or nested component class declared within a configuration/component class).
	 * @param metadata the metadata of the annotated class
	 * @return {@code true} if the given class is to be registered for
	 * configuration class processing; {@code false} otherwise
	 */
	public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
		// Do not consider an interface or an annotation...
		// 不考虑接口或注解
		if (metadata.isInterface()) {
			return false;
		}

		// Any of the typical annotations found?
		// 检查是否被注解@Component、@ComponentScan、@Import、@ImportResource标注
		for (String indicator : candidateIndicators) {
			if (metadata.isAnnotated(indicator)) {
				return true;
			}
		}

		// Finally, let's look for @Bean methods...
		// 最后检查是否有@Bean标注的方法
		try {
			return metadata.hasAnnotatedMethods(Bean.class.getName());
		}
		catch (Throwable ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
			}
			return false;
		}
	}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1967724.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【用C语言编写】题目名称:数9的个数题目内容:编写程序数一下1到100的所有整数中出现多少个数字9

题目名称&#xff1a;数9的个数 题目内容&#xff1a;编写程序数一下1到100的所有整数中出现多少个数字9 代码如下&#xff1a; #include <stdio.h> int main() {int i 0;int count 0;for (i 1; i <100; i){if (i % 10 9) //个位为9的count;else if (i / 10 9) //…

手机应用的时间可以修改吗??

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

Java线程安全之同步方法

同步方法 使用synchronized修饰的方法&#xff0c;就叫做同步方法&#xff0c;其固定格式如下&#xff1a; public [static] synchronized 返回值类型 同步方法() {可能会产生线程安全问题的代码 }注意事项&#xff1a; 同步方法可以是普通成员方法&#xff0c;也可以是sta…

聊一聊 webpack5性能优化有哪些?

介绍 此文章基于webpack5来阐述 webpack性能优化较多&#xff0c;可以对其进行分类 优化打包速度&#xff0c;开发或者构建时优化打包速度&#xff08;比如exclude、catch等&#xff09;优化打包后的结果&#xff0c;上线时的优化&#xff08;比如分包处理、减小包体积、CDN…

什么是安全生产痕迹化管理?如何做到生产过程中全程痕迹化管理?

安全生产痕迹化管理&#xff0c;简单来说&#xff0c;就是通过记录一些“信息”来确保安全工作的进展。这些方法包括记会议内容、写安全日记、拍照片、签字盖章、指纹识别、面部识别还有手机定位等。记录下来的文字、图片、数据和视频&#xff0c;就像一个个“脚印”&#xff0…

“免费”制作中国式报表的工具横空出世,内置丰富图表组件!

一.报表制作的烦恼 报表是我们日常工作中的好伙伴&#xff0c;它在企业管理和决策过程中扮演着重要角色&#xff0c;能够清晰直观地展示数据&#xff0c;让关键信息一目了然。 然而&#xff0c;无论是使用传统的手工报表还是基于软件的普通报表操作&#xff0c;都存在不便之处…

clickhouse sql 语法参考

clickhouse sql 语法参考 1. select1.1 将结果中的某些列与 re2 正则表达式匹配&#xff0c;可以使用 COLUMNS 表1.2 ARRAY JOIN - 数组数据平铺1.3 LEFT ARRAY JOIN 2. create2.1 分布式创建数据库2.2 分布式创建复制表2.4 CREATE TABLE [IF NOT EXISTS] [db.]table_name ENGI…

layui table 重新设置表格的高度

在layui的table模块中&#xff0c;如果使用table.render({})渲染了一个表格实例时&#xff0c;确定了height配置&#xff0c;后续用table.resize(id)方法重置表格尺寸时&#xff0c;表格的高度是不会变化的&#xff08;如果我的理解没有错的话&#xff09;。 有时我们希望根据…

ChatGPT在办公与科研中有怎样的应用?又是如何做论文撰写、数据分析、机器学习、深度学习及AI绘图

2022年11月30日&#xff0c;可能将成为一个改变人类历史的日子——美国人工智能开发机构OpenAI推出了聊天机器人ChatGPT-3.5&#xff0c;将人工智能的发展推向了一个新的高度。2023年11月7日&#xff0c;OpenAI首届开发者大会被称为“科技界的春晚”&#xff0c;吸引了全球广大…

8.1 字符串中等 43 Multiply Strings 38 Count and Say

43 Multiply Strings【默写】 那个难点我就没想先解决&#xff0c;原本想法是先想其他思路&#xff0c;但也没想出。本来只想chat一下使用longlong数据类型直接stoi()得不得行&#xff0c;然后就看到了答案&#xff0c;直接一个默写的大动作。但这道题确实考察的是还原乘法&…

C++(入门2)

承接上C&#xff08;入门1&#xff09;&#xff1a;CSDN 引用&#xff1a;&#xff08;类似指针但不同于指针&#xff0c;后续会将两则不同&#xff09; 概念 引⽤不是新定义⼀个变量&#xff0c;⽽是给已存在变量取了⼀个别名&#xff0c;编译器不会为引⽤变量开辟内存空间…

c语言第六天笔记

分支结构 分支结构&#xff1a;又被称之为选择结构 概念 选择结构&#xff1a;根据条件成立与否&#xff0c;选择相应的操作。 条件构建 关系表达式&#xff1a;含有关系运算符的表达式&#xff08;>,,&#xff09; 逻辑表达式&#xff1a;含有逻辑运算符的表达式&…

网络安全相关工作必须要有证书吗?

在当今数字化时代&#xff0c;网络安全已成为至关重要的领域。然而&#xff0c;对于从事网络安全相关工作的人员来说&#xff0c;证书是否是必不可少的呢? 一、网络安全证书的重要性 网络安全证书在一定程度上能够证明从业者具备相关的知识和技能。例如&#xff0c;CISP 作为国…

基于LLM开发AI应用竟如此简单

一、什么是LLM 随着人工智能技术的不断发展&#xff0c;越来越多的企业和机构开始将其应用于各个领域。其中&#xff0c;基于语言模型的人工智能技术&#xff08;LLM&#xff09;在自然语言处理、文本生成等方面表现出色&#xff0c;被广泛应用于各种场景中。 LLM是一种基于大…

你们要的“轮子”来了!67 个仓颉语言三方库正式公开!

01 Cangjie-TPC社区简介 Cangjie-TPC&#xff08;Third Party Components&#xff09;用于汇集基于仓颉编程语言开发的开源三方库&#xff0c;帮助开发者方便、快捷、高质量构建仓颉程序。 Cangjie-TPC社区联合软通动力、宝兰德、普元、上汽以及社区开发者共同完成第一批常用…

快速设置 terminator 透明背景

看图&#xff0c;按步骤设置后⭐重启一个终端则为透明效果 效果展示&#xff1a;

vscode+platformio开发小技巧

使用vscodeplatformio开发&#xff0c;具体安装配置文章很多&#xff0c;这里分享一些方便使用的小技巧&#xff0c;让使用体验在不增加学习成本的情况下更加丝滑。 1、配置依赖库 在使用vscode开发前&#xff0c;arduino环境遗留了一些库文件&#xff0c;这些第三方库可以通…

(20240801)矿山固废基胶凝材料及混凝土中文期刊整理

一、篇名:固废 级别:EI + 篇名:固废混凝土/水泥/胶砂/胶凝材料 级别:EI

Flat Ads资讯:Meta、Google、TikTok 7月产品政策速递

Flat Ads拥有全球媒介采买(MediaBuy)业务,为方便广告主及时了解大媒体最新政策,Flat Ads将整理大媒体产品更新月报,欢迎大家关注我们及时了解最新行业动向。 一、Meta 1、Reels 应用推广现可突出显示应用评分、点评和下载量 为了不断优化 Instagram 上的广告体验和广告表现,…

攻防世界之《这个按钮做什么》题解

下载解压后&#xff0c;发现只有一个文件。 放入exeinfope软件里看看 根据activity猜测可能是安卓软件&#xff0c;修改文件后缀为.apk 然后用模拟器打开这个软件并会自动安装。 打开软件界面如下&#xff1a; 看得出来只有一个密码输入框&#xff0c;应该找到对应的密码就会…