一起学SF框架系列5.8-spring-Beans-Bean注解解析3-解析配置component-scan

这篇具有很好参考价值的文章主要介绍了一起学SF框架系列5.8-spring-Beans-Bean注解解析3-解析配置component-scan。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文主要讲述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,而不进行实际扫描,从而提高组件扫描效率,减少应用启动时间。如果使用该功能,需要引入如下依赖:

<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模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Spring框架全系列】方法注解@Bean的使用

    📬📬哈喽,大家好,我是小浪。上篇博客我们介绍了五大类注解的使用方法,以及如何解决Spring使用五大类注解生成bean-Name的问题;那么,谈到如何更简单的读取和存储对象,这里我们还需要介绍另外一个\\\"方法注解@Bean\\\"的使用,快来一起学习叭!🛳🛳 📲目录 一、如何使

    2024年02月04日
    浏览(33)
  • Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definiti

    Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true 解决方法: 在application.yml或你项目对应的配置文件中添加如下代码: 实践验证: 为了验证该配置信息是否会造成覆盖问题,现模拟一下情况: 项目中有feign模块、service_user模块、serv

    2024年02月04日
    浏览(46)
  • Spring框架中的Bean

    在Spring框架中,Bean是指一个由Spring容器管理的对象。这个对象可以是任何一个Java类的实例,例如数据库连接、业务逻辑类、控制器等等。Bean实例的创建和管理是由Spring容器负责的,而不是由应用程序本身负责。 Bean的主要优势是可以将对象的创建和管理与业务逻辑分离。这

    2023年04月09日
    浏览(26)
  • Spring系列三:基于注解配置bean

    上文中, 我们学习到了 Spring系列二:基于XML配置bean 接下来我们学习, 通过注解配置bean 基于注解的方式配置 bean , 主要是项目开发中的组件, 比如 Controller, Service 和 Dao. 组件注解的形式有 1. @Component 表示当前注解标识的是一个组件 2. @Controller 表示当前注解标识的是一个控制器

    2024年02月13日
    浏览(27)
  • Spring系列篇 -- Bean的生命周期

    目录 经典面试题目: 一,Bean的生命周期图 二,关于Bean的生命周期流程介绍: 三,Bean的单例与多例模式 总结: 前言:今天小编给大家带来的是关于Spring系列篇中的Bean的生命周期讲解。在了解Bean的生命周期建议先看Spring 的AOP的讲解。希望大家看了能够对你们学习,工作带

    2024年02月12日
    浏览(39)
  • 4.3---Spring框架之Spring中bean的注入方式---(深入版本)

    Spring基于xml注入bean的几种方式: set()方法注入; 2.构造器注入:①通过index设置参数的位置;②通过type设置参数类型; 静态工厂注入; 实例工厂; Spring IOC注入方式用得最多的是(1)(2)种; 注意:通过Spring创建的对象默认是单例的,如果需要创建多实例对象可以在标签后面添

    2023年04月10日
    浏览(30)
  • 【框架源码】Spring源码解析之Bean创建源码流程

    问题:Spring中是如何初始化单例bean的? 我们都知道Spring解析xml文件描述成BeanDefinition,解析BeanDefinition最后创建Bean将Bean放入单例池中,那么Spring在创建Bean的这个过程都做了什么。 Spring核心方法refresh()中最最重要的一个方法 finishBeanFactoryInitialization() 方法,该方法负责初始化

    2024年02月09日
    浏览(29)
  • Spring框架Bean对象的五个作用域

    ​ 在Spring项目中,那些 由Spring IoC容器所管理的对象,称为bean 。简单地讲,bean就是由Spring容器初始化、装配及管理的对象,除此之外,bean就与应用程序中的其他对象没有什么区别了。 而bean定义以及bean相互间的依赖关系将通过配置元数据来描述。 上一段描述简析: spri

    2024年03月09日
    浏览(31)
  • 【框架源码】Spring源码解析之Bean生命周期流程

    观看本文前,我们先思考一个问题,什么是Spring的bean的生命周期?这也是我们在面试的时候,面试官常问的一个问题。 在没有Spring之前,我们创建对象的时候,采用new的方式,当对象不在被使用的时候,由Java的垃圾回收机制回收。 而 Spring 中的对象是 bean,bean 和普通的 J

    2024年02月09日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包