springboot启动流程 (2) 组件扫描

这篇具有很好参考价值的文章主要介绍了springboot启动流程 (2) 组件扫描。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

SpringBoot的组件扫描是基于Spring @ComponentScan注解实现的,该注解使用basePackages和basePackageClasses配置扫描的包,如果未配置这两个参数,Spring将扫描该配置类所属包下面的组件。

在服务启动时,将使用ConfigurationClassPostProcessor扫描当前所有的BeanDefinition,解析Configuration类,如果Configuration类标注了ComponentScan注解,将获取basePackages和basePackageClasses配置并扫描对应的包下面的组件。

ConfigurationClassPostProcessor类

实现了BeanDefinitionRegistryPostProcessor接口,在Spring启动的invokeBeanFactoryPostProcessors阶段被调用。

BeanDefinitionRegistryPostProcessor接口

继承BeanFactoryPostProcessor接口,允许在常规BeanFactoryPostProcessor调用之前注册更多的BeanDefinition。特别是,这些BeanDefinition反过来可以定义BeanFactoryPestProcessor实例。

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

	/**
	 * Modify the application context's internal bean definition registry after its
	 * standard initialization. All regular bean definitions will have been loaded,
	 * but no beans will have been instantiated yet. This allows for adding further
	 * bean definitions before the next post-processing phase kicks in.
	 */
	void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

postProcessBeanDefinitionRegistry实现

在ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry实现中:

  1. 扫描当前所有的BeanDefinition,找出所有的Configuration类
  2. 使用ConfigurationClassParser解析所有的Configuration类并找出需要注册的组件
    • 解析Component注解
    • 解析PropertySource注解
    • 解析ComponentScan注解
    • 解析Bean注解
  3. 将解析出来的组件注册到Spring容器

解析ComponentScan注解

这里使用到了ComponentScanAnnotationParser类。专门用来解析@ComponentScan注解。文章来源地址https://www.toymoban.com/news/detail-489804.html

ComponentScanAnnotationParser类

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
	ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
			componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

	Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
	boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
	scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
			BeanUtils.instantiateClass(generatorClass));

	ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
	if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
		scanner.setScopedProxyMode(scopedProxyMode);
	} else {
		Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
		scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
	}

	scanner.setResourcePattern(componentScan.getString("resourcePattern"));

	for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
		for (TypeFilter typeFilter : typeFiltersFor(filter)) {
			scanner.addIncludeFilter(typeFilter);
		}
	}
	for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
		for (TypeFilter typeFilter : typeFiltersFor(filter)) {
			scanner.addExcludeFilter(typeFilter);
		}
	}

	boolean lazyInit = componentScan.getBoolean("lazyInit");
	if (lazyInit) {
		scanner.getBeanDefinitionDefaults().setLazyInit(true);
	}

	// 解析basePackages属性
	Set<String> basePackages = new LinkedHashSet<>();
	String[] basePackagesArray = componentScan.getStringArray("basePackages");
	for (String pkg : basePackagesArray) {
		String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
		Collections.addAll(basePackages, tokenized);
	}
	// 解析basePackageClasses属性
	for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
		basePackages.add(ClassUtils.getPackageName(clazz));
	}
	// 如果上面两个属性都没有配置,则默认扫描Configuration类所在包及其子包
	if (basePackages.isEmpty()) {
		basePackages.add(ClassUtils.getPackageName(declaringClass));
	}

	scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
		@Override
		protected boolean matchClassName(String className) {
			return declaringClass.equals(className);
		}
	});
	return scanner.doScan(StringUtils.toStringArray(basePackages));
}

doScan方法

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
	Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
	for (String basePackage : basePackages) {
		// 查找basePackage下面的类文件,使用ASM解析封装成BeanDefinition
		Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
		for (BeanDefinition candidate : candidates) {
			ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
			candidate.setScope(scopeMetadata.getScopeName());
			String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
			if (candidate instanceof AbstractBeanDefinition) {
				postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
			}
			if (candidate instanceof AnnotatedBeanDefinition) {
				AnnotationConfigUtils
                    .processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
			}
			if (checkCandidate(beanName, candidate)) {
				BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
				definitionHolder = AnnotationConfigUtils
                    .applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
				beanDefinitions.add(definitionHolder);
				registerBeanDefinition(definitionHolder, this.registry);
			}
		}
	}
	return beanDefinitions;
}

到了这里,关于springboot启动流程 (2) 组件扫描的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • SpringBoot启动流程简要

    SpringBoot启动流程大概: 初始化SpringApplication 根据项目的配置情况和Conditional条件来推断是否是一个Web应用。 读取所有jar包下面spring.factories文件,解析放入缓存,然后读取 ApplicationListener 为key的监听器,后续在SpringBoot加载的过程中会基于事件发布来做很多扩展通知。 真正的

    2024年02月15日
    浏览(25)
  • SpringBoot 启动流程

    概述 SpringBoot 是一个基于 Spring 的框架,它通过自动配置和约定大于配置的方式,简化了 Spring 应用程序的开发和部署。在本文中,我们将会深入了解 Spring Boot 的启动流程,掌握 Spring Boot 应用程序是如何启动并初始化的。 构建环境 在了解 Spring Boot 的启动流程之前,需要先理

    2024年02月06日
    浏览(37)
  • springboot启动流程源码解析(带流程图)

    本文自己写的(头条也有这篇文章),若有问题,请指正。 大致流程如下: 1. 初始化SpringApplication,从META-INF下的spring.factories读取 ApplicationListener/ApplicationContextInitializer 2.运行SpringApplication的run方法 3.读取项目中环境变量、jvm配置信息、配置文件信息等 4.创建Spring容器对象(

    2024年02月08日
    浏览(33)
  • springboot启动流程 (3) 自动装配

    在SpringBoot中,EnableAutoConfiguration注解用于开启自动装配功能。 本文将详细分析该注解的工作流程。 启用SpringBoot自动装配功能,尝试猜测和配置可能需要的组件Bean。 自动装配类通常是根据类路径和定义的Bean来应用的。例如,如果类路径上有tomcat-embedded.jar,那么可能需要一个

    2024年02月09日
    浏览(32)
  • SpringBoot内嵌Tomcat启动流程

    Spring MVC 让开发者不用了解 Servlet 细节,专注于 Controller 编写 API 接口。Spring Boot 更是采用约定大于配置的设计思想,通过内嵌 Tomcat 的方式让开发者可以快速构建并部署一个 Web 应用。怎么做到的呢? 早期的开发,一般是基于 Spring 和 Spring MVC 构建我们的应用,然后把项目打

    2024年02月02日
    浏览(31)
  • Java面试--SpringBoot启动流程

    SpringBoot 是依赖于 Spring 的,比起 Spring,除了拥有 Spring 的全部功能以外,SpringBoot 无需繁琐的 Xml 配置,这取决于它自身强大的自动装配功能;并且自身已嵌入Tomcat、Jetty 等 web 容器,集成了 SpringMvc,使得 SpringBoot 可以直接运行,不需要额外的容器,提供了一些大型项目中常

    2024年02月01日
    浏览(28)
  • SpringBoot的启动流程源码分析

    new 一个IOC容器,传入配置好的文件xml,在这个地方打bug 在这个debug的栈帧中,下面几个不用看,直接看到getBean 内容如图所示,name传的就是我们在xml的bean标签的id,这里是instanceA 进入到doGetBean后,因为我是从IOC初始化容器debug进来的所以第一次通过。 Object sharedInstance = getSingl

    2024年01月24日
    浏览(34)
  • SpringBoot启动流程及自动配置

    SpringBoot启动流程源码: 1、启动SpringBoot启动类SpringbootdemoApplication中的main方法。 2、调用SpringApplication.run(SpringbootdemoApplication.class, args),该方法是一个静态方法。 3、继续调用SpringApplication内部的run方法 ,且构建了一个SpringApplication对象,应用程序将从指定的主要来源加载Bea

    2024年02月14日
    浏览(33)
  • SpringBoot源码学习4——SpringBoot内嵌Tomcat启动流程源码分析

    系列文章目录和关于我 我在初学spring的时候,很懵逼,因为整个项目中不存在main方法,让我有点摸不着头脑。那时候我知道有个东西叫tomcat是它监听了端口,解析了协议调到了我的servlet。 在我初学SpringBoot的时候,很懵逼,有main方法了,但是tomcat在哪里呢,又是如何启动起

    2024年02月04日
    浏览(32)
  • SpringBoot3.X源码分析(启动流程)

    1 启动入口 静态辅助类,可用于运行使用默认配置(即我们添加的一系列注解)的指定源的 SpringApplication 。 primarySource - 要载入的主要源,即指定源,这里为传入的 Application.class  Class? :泛型决定了任何类都可以传入 args - 应用程序参数(通常从main方法传递) 返回:正在运

    2024年01月23日
    浏览(29)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包