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

server/2024/9/25 4:16:58/

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

java">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定义

java">	@Override@Nullablepublic 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.// 构建和配置ClassPathBeanDefinitionScannerClassPathBeanDefinitionScanner 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)

扫描开始

java">	/*** 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<>();// 遍历basePackagesfor (String basePackage : basePackages) {// 扫描basePackage,将符合要求的bean定义全部找出来Set<BeanDefinition> candidates = findCandidateComponents(basePackage);// 遍历所有候选的bean定义for (BeanDefinition candidate : candidates) {// 解析@Scope注解,包括scopeName和proxyModeScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());// 使用beanName生成器来生成beanNameString 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,@DescriptionAnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}// 检查beanName是否已经注册过,如果注册过,检查是否兼容if (checkCandidate(beanName, candidate)) {// 将当前遍历bean的bean定义和beanName封装成BeanDefinitionHolderBeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);// 根据proxyMode的值,选择是否创建作用域代理definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);// 注册beanDefinitionregisterBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;}

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

java">	public Set<BeanDefinition> findCandidateComponents(String basePackage) {if (this.componentsIndex != null && indexSupportsIncludeFilters()) {return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);}else {return scanCandidateComponents(basePackage);}}
java">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文件,得到 Resourceboolean traceEnabled = logger.isTraceEnabled();boolean debugEnabled = logger.isDebugEnabled();for (Resource resource : resources) {if (traceEnabled) {logger.trace("Scanning " + resource);}if (resource.isReadable()) {try {//根据资源对象获取资源对象的MetadataReaderMetadataReader 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定义信息

java">	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;}
java">	/*** 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());}
java">    /*** 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);}
java"> /*** 基于@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为空,向下执行 // 判断传入的 phaseif (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))

java">/*** 检查给定的元数据,以查找给定的候选配置类是否被指定的注解标注** 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.ppmy.cn/server/93505.html

相关文章

ChinaJoy 2024,VERYCLOUD睿鸿股份与你相聚

&#x1f3ae;2024 ChinaJoy于26日正式开幕 &#x1f557;7月26-28日 &#x1f310;VERYCLOUD睿鸿股份在BTOB商务洽谈馆 &#x1f31f;W4-B785展位 &#x1f387;展台交流好礼相送 与多行业好友现场相聚、畅谈&#x1f9d0; 现场游戏企业云集 专业观众、玩家纷至沓来 与游戏/短…

George Danezis谈Mysticeti的共识性能

Sui的新共识引擎Mysticeti已经在主网上开始分阶段推出。Mysten Labs联合创始人兼首席科学家George Danezis在采访中&#xff0c;讨论了其低延迟如何通过高性能应用程序提升用户体验。 采访视频&#xff1a; https://youtu.be/WHvtPQUa2Q0 中文译文&#xff1a;延迟和吞吐量对…

超市是怎样高效完成客流统计与客流分析

随着科技的进步&#xff0c;越来越多的超市开始采用现代化的客流统计系统来优化日常运营和提升顾客体验。本文将探讨超市客流统计面临的难题、客流统计系统的构成及其应用场景&#xff0c;以及系统如何通过高识别率和热力图分析等功能为超市带来实际效益。 一、景区客流统计难题…

Springcloud物流配送后台-计算机毕业设计源码69809

目 录 摘要 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2 物流配送后台系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 操作可行性分析 2.2 系统流程分析 2.2.1数据增加流程 2.2.2 数据修改流程 2.2.3 数据…

【C++笔试强训】day05

游游的you 思路 贪心&#xff1a;优先组成 you&#xff0c;最少的字母决定了you的数量。需要注意&#xff1a;如果oo剩下n个&#xff0c;那么相邻oo的个数是n-1个&#xff0c;而不是n/2个。 例如 oooooo oo oo oooo oo 6个o&#xff0c;两两组合有3对&#xff0c;掐头去尾有…

Mojo AI编程语言(十六)库与框架:扩展Mojo功能

目录 1. 库与框架的定义与作用 1.1 库 1.2 框架 2. Mojo中的库与框架 2.1 标准库 2.1.1 数据结构 2.1.2 算法 2.1.3 输入输出 2.2 第三方库 2.2.1 数据处理 2.2.2 机器学习 2.3 框架 2.3.1 Flask 2.3.2 Django 3. 案例 3.1 项目需求 3.2 数据准备 3.3 模型训…

Kafka 为什么这么快的七大秘诀,涨知识了

我们都知道 Kafka 是基于磁盘进行存储的&#xff0c;但 Kafka 官方又称其具有高性能、高吞吐、低延时的特点&#xff0c;其吞吐量动辄几十上百万。 在座的靓仔和靓女们是不是有点困惑了&#xff0c;一般认为在磁盘上读写数据是会降低性能的&#xff0c;因为寻址会比较消耗时间。…

ctfshow-web入门-sql注入(web171-web175)

目录 1、web171 2、web172 3、web173 4、web174 5、web175 1、web171 单引号测一下&#xff0c;报错 -- 闭合后回显正常 也可以用 # &#xff0c;不过需要 URL 编码 成功闭合之后&#xff0c;先判断下字段数&#xff1a; 1 order by 3-- 3 的时候正常 4 的时候报错&am…