SpringBoot原理(1)--@SpringBootApplication注解使用和原理

news2024/11/9 6:13:10

文章目录

  • 前言
  • 主启动类的配置
    • @SpringBootConfiguration注解
      • 验证启动类是否被注入到spring容器中
    • @ComponentScan 注解
      • @ComponentScan 注解解析与路径扫描
    • @EnableAutoConfiguration注解
  • 问题解答
    • 1.@AutoConfigurationPackage和@ComponentScan的作用是否冲突
      • 起因
      • 回答
    • 2.为什么能实现自动加载
      • 起因
      • 回答

前言

本文主要讲解@SpringBootApplication注解使用和原理。
源码基于spring-boot-2.2.13.RELEASE进行讲解

主要是弄懂以下几个问题:
1.@SpringBootApplication注解主要做了什么事?
2.为什么能实现自动加载
3.SpringBoot怎么知道哪些java类应该当作Bean被注入到IOC容器中?
4.springboot默认扫描启动类同包及子包下面的组件,原理是什么?

如图:为什么springboot能自动注入service, controller等注解到IOC容器中
在这里插入图片描述

主启动类的配置

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

//@SpringBootApplication 来标注一个主程序类
//说明这是一个Spring Boot应用
@SpringBootApplication
public class SpringbootApplication {
   public static void main(String[] args) {
     //以为是启动了一个方法,实际是启动了一个服务
      SpringApplication.run(SpringbootApplication.class, args);
   }
}

@SpringBootApplication
@SpringBootApplication内部的组成结构,如下图:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
    ..........
}

@SpringBootApplication注解是Spring Boot的核心注解,它其实是一个组合注解主要是由@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三个注解合并而成。

@SpringBootConfiguration:
内部结构是@Configuration注解,标明当前类是一个配置类,项目启动时该类会被加载到spring容器中

@EnableAutoConfiguration
开启自动配置功能,实现自动装配的核心注解,内部包含两个注解:@AutoConfigurationPackage + @Import(AutoConfigurationImportSelector.class)。
@AutoConfigurationPackage:将主程序类所在包及所有子包下的组件到扫描到spring容器中。这里的组件主要是@Enitity、@Mapper等第三方依赖的注解
@Import(AutoConfigurationImportSelector.class):在该类中加载 META-INF/spring.factories 的配置信息。然后筛选出以 EnableAutoConfiguration 为 key 的数据,加载到 IOC 容器中,实现自动配置功能!

@ComponentScan
默认扫描@ComponentScan注解所在包及子包中标注了@Component注解的类以及衍生注解标注的类(如 @Repository or @Service or @Controller等等)

@SpringBootConfiguration注解

点进@SpringBootConfiguration内部,看其内部结构:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
// 相当于@SpringBootConfiguration就是@Configuration
public @interface SpringBootConfiguration {
}

@Configuration是Spring的一个注解,标明该类为配置类,其修饰的类会加入Spring容器。这就说明SpringBoot的启动类会加入Spring容器。

验证启动类是否被注入到spring容器中

@SpringBootApplication
public class Demo3Application {

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(Demo3Application.class, args);
        Demo3Application application = run.getBean("demo3Application",Demo3Application.class);
        System.out.println(application);
        System.out.println("spring容器中是否包含启动类:"+run.containsBean("demo3Application"));
    }

}

可以看到以下执行结果中,确实将启动类加载到spring容器中了
在这里插入图片描述

@ComponentScan 注解

用于定义 Spring 的扫描路径,等价于在 xml 文件中配置 context:component-scan,假如不配置扫描路径,那么 Spring 就会默认扫描当前类所在的包及其子包中的所有标注了 @Component,@Service,@Controller 等注解的类。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)//可重复注解
public @interface ComponentScan {

   @AliasFor("basePackages")
   String[] value() default {};//基础包名,等同于basePackages

   @AliasFor("value")
   String[] basePackages() default {};//要扫描的路径,如果为空,解析的时候会解析被@ComponentScan标注类的包路径

   Class<?>[] basePackageClasses() default {};//扫描的类,会扫描该类所在包及其子包的组件。与basePackages互斥

   Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;//注册为BeanName生成策略 默认BeanNameGenerator,用于给扫描到的Bean生成BeanName,在解析注册BeanDefinition的时候用到

   Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;//用于解析bean的scope的属性的解析器,默认是AnnotationScopeMetadataResolver,类定义上的@Scope注解解析器,如果没有该注解默认单例

   ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;//scoped-proxy 用来配置代理方式 // no(默认值):如果有接口就使用JDK代理,如果没有接口就使用CGLib代理 interfaces: 接口代理(JDK代理) targetClass:类代理(CGLib代理)

   String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;//配置要扫描的资源的正则表达式的,默认是"**/*.class",即配置类包下的所有class文件。
  
   boolean useDefaultFilters() default true;//useDefaultFilters默认是true,扫描@Component标注的类以及衍生注解标注的类(如@Component or @Repository or @Service or @Controller等等),如果为false则不扫描,需要自己指定includeFilters

   Filter[] includeFilters() default {};//自定义包含过滤器,如果@Component扫描不到或者不能满足,则可以使用自定义扫描过滤器

   Filter[] excludeFilters() default {};//自定义排除过滤器,和includeFilters作用相反

   boolean lazyInit() default false;//是否是懒加载

   @Retention(RetentionPolicy.RUNTIME)
   @Target({})
   @interface Filter {//过滤器注解

      FilterType type() default FilterType.ANNOTATION;//过滤判断类型

      @AliasFor("classes")
      Class<?>[] value() default {};//要过滤的类,等同于classes

      @AliasFor("value")
      Class<?>[] classes() default {};//要过滤的类,等同于value

      String[] pattern() default {};// 正则化匹配过滤

   }

}

@ComponentScan 注解解析与路径扫描

@ComponentScan注解的解析依旧在SpringApplication调用AbstractApplicationContext#refresh的时候触发调用ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry,然后通过ConfigurationClassParser#parse解析,委托给doProcessConfigurationClass方法处理:
具体原理可参考《@ComponentScan注解使用和原理》

@EnableAutoConfiguration注解

@EnableAutoConfiguration自动配置的关键,内部实际上就去加载 META-INF/spring.factories 文件的信息,然后筛选出以 EnableAutoConfiguration 为key的数据,加载到IOC容器中,实现自动配置功能

查看其内部结构

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 自动配置包
@AutoConfigurationPackage
// 使用@Import注解导入AutoConfigurationImportSelector类,实现了ImportSelector接口,重写了selectImports()方法,帮助我们返回所有需要被注册为bean的类全限定类名的数组集合
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}

重点看@AutoConfigurationPackage注解和@Import(AutoConfigurationImportSelector.class)注解。

1:@AutoConfigurationPackage注解:

@AutoConfigurationPackage作用:
将主程序类所在包及所有子包下的组件到扫描到spring容器中。这里的组件主要是@Enitity、@Mapper等第三方依赖的注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

看其@Import进来的类AutoConfigurationPackages.Registrar类:
这是一个内部类,源码如下:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

    // 注册bean定义
	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
	}

   // 返回一组需要注入的对象
	@Override
	public Set<Object> determineImports(AnnotationMetadata metadata) {
		return Collections.singleton(new PackageImports(metadata));
	}
}

通过debug发现@AutoConfigurationPackage其实就是把主启动类的包名传进来,然后将主启动类同包及子包下的组件注册到容器中。
在这里插入图片描述

2:@Import(AutoConfigurationImportSelector.class)

@EnableAutoConfiguration注解通过@Import(AutoConfigurationImportSelector.class)向容器中导入了AutoConfigurationImportSelector组件,它实现了DeferredImportSelector,并间接实现了ImportSelector接口,重写了selectImports()方法,帮助我们返回所有需要被注册为bean的类全限定类名的数组集合。

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	//......省略其他代码

	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
				annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
//......
}

在这里插入图片描述

getCandidateConfigurations会到classpath下的读取META-INF/spring.factories文件的配置,并返回以 EnableAutoConfiguration 为 key 的字符串数组。
在这里插入图片描述
getCandidateConfigurations默认加载的是spring-boot-autoconfigure依赖包下的META-INF/spring.factories文件,我们可以看一个以EnableAutoConfiguration为key刚好127个
在这里插入图片描述

为什么getCandidateConfigurations读取的是classpath下的META-INF/spring.factories文件?我们可以看一下源码,如下图所示,源码中写明了要读取该文件。
在这里插入图片描述
简单梳理:

  • FACTORIES_RESOURCE_LOCATION 的值是 META-INF/spring.factories
  • Spring启动的时候会扫描所有jar路径下的 META-INF/spring.factories ,将其文件包装成Properties对象,从Properties对象获取到key值为 EnableAutoConfiguration 的数据,然后添加到容器里边。

问题解答

针对@SpringBootApplication注解使用和原理过程中有一些疑问点,特此解答

1.@AutoConfigurationPackage和@ComponentScan的作用是否冲突

起因

在研究springboot启动类时看到了两个意义相同的注解:@AutoConfigurationPackage和@ComponentScan,都是导入启动类同包及子包下的组件,于是疑惑为什么要重复使用?

回答

@AutoConfigurationPackage和@ComponentScan一样,都是将Spring Boot启动类所在的包及其子包里面的组件扫描到IOC容器中,但是区别是@AutoConfigurationPackage扫描@Enitity、@Mapper等第三方依赖的注解,@ComponentScan 注解主要是扫描 Spring 家族的各种 Bean,如 @Controller、@Service、@Component、@Repository 以及由此衍生出来的一些其他的 Bean。所以这两个注解扫描的对象是不一样的。当然这只是直观上的区别,更深层次说,@AutoConfigurationPackage是自动配置的提醒,是Spring Boot中注解,而@ComponentScan是Spring的注解

2.为什么能实现自动加载

起因

springboot为什么能够实现自动加载,如数据库,tomcat等默认不用配置什么东西就能加载进来。

回答

@EnableAutoConfiguration注解是自动配置的关键,内部实际上就去加载 META-INF/spring.factories 文件的信息,然后筛选出以 EnableAutoConfiguration 为key的数据,加载到IOC容器中,实现自动配置功能。

默认加载的是spring-boot-autoconfigure依赖包下的META-INF/spring.factories文件,我们可以看一个以EnableAutoConfiguration为key的元素
在这里插入图片描述

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

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

相关文章

双路高速 AD 实验

目录 双路高速 AD 实验 1、简介 3PA1030 芯片 2、实验任务 3、程序设计 3.1、hs_dual_ad 模块代码 clk_wiz IP 核 的添加方法 ILA IP 核&#xff08;集成逻辑分析器&#xff1a;Integrated Logic Analyzer&#xff0c;ILA&#xff09; 4、硬件设计 4.1、添加.xdc约束…

23年软考网络工程师是什么?主要是考什么,有什么用?

网络工程师每年考两次&#xff0c;相比其他的软考考试一年中考的机会又多了一次 网络工程师证书考到后&#xff0c;通过本级考试的合格人员能根据应用部门的要求进行网络系统的规划、设计和网络设备的软硬件安装调试工作&#xff0c;能进行网络系统的运行、维护和管理&#xf…

汽车行业项目管理面临的5个挑战及解决方案

汽车行业正跨越式地迈向新的未来。其目前的发展主要由四大趋势驱动&#xff1a;连接性、自动驾驶汽车、共享出行和电气化。这给汽车企业带来了诸多挑战&#xff1a;竞争加剧&#xff0c;快速发展带来的频繁变化&#xff0c;与软件公司建立伙伴关系&#xff0c;以及其他相关问题…

GIS 功能模块实现

文章目录 1. GIS 模块流程图2. 网页端地图缓存的实现3. GIS 图形操作功能实现1 &#xff09;地图漫游2 &#xff09;对象删除3 &#xff09;选择复制属性查看 GIS 基本功能模块主要是在表现层开发的&#xff0c;是在OpenLayers 开发框架提供的接口上&#xff0c;通过Geo Server…

【计算机网络复习之路】应用层(谢希仁第八版)

专栏&#xff1a;计算机网络复习之路 目录 一、域名系统DNS 1.1 本地域名服务器采用迭代查询 1.2 本地域名服务器采用递归查询 二、文件传送协议FTP 三、远程终端协议TELNET 四、万维网WWW (World Wide Web) 4.1 万维网需要解决的问题 【1】怎样标志分布在整个互联网…

在 Swift 中使用 async let 并发运行后台任务

文章目录 前言长期运行的任务阻塞了UI使用 async/await 在后台执行任务在后台执行多个任务使用 "async let " 下载多个文件结论 前言 Async/await 语法是在 Swift 5.5 引入的&#xff0c;在 WWDC 2021中的 Meet async/await in Swift 对齐进行了介绍。它是编写异步代…

实时日志管理分析解决方案

什么是日志管理 组织网络可能很复杂&#xff0c;由大量互连的系统、应用程序和设备组成。这些组件中的每一个都会生成大量日志数据&#xff0c;捕获有关系统事件、用户活动和网络流量的详细信息。生成的日志数据量庞大&#xff0c;因此难以有效管理和分析。 日志管理是收集、…

使用 DFS 解决排列数字问题并使用 pythontutor 可视化

使用 DFS 解决排列数字问题并使用 pythontutor 可视化 问题描述 给定一个整数 n n n&#xff0c;将数字 1 ∼ n 1∼n 1∼n 排成一排&#xff0c;将会有很多种排列方法。 现在&#xff0c;请你按照字典序将所有的排列方法输出。 输入格式 共一行&#xff0c;包含一个整数…

盘点内核中常见的CPU性能卡点

我们的应用程序都是运行在各种语言的运行时、操作系统内核、以及 CPU 等硬件之上的。大家平时一般都是使用Go、Java等语言进行开发。但这些语言的下面是由运行时、内核、硬件等多层支撑起来的。 我们的程序在运行的时候&#xff0c;很多时候性能卡点可能并不一定是自己的应用代…

Win系统下同时访问公司内网及公网设置

一、修改系统配置 修改系统配置&#xff0c;使公网默认不走VPN路由&#xff1b; 连接VPN&#xff0c;并查看路由表&#xff1b; route print可以看到&#xff0c;多了些路由信息&#xff0c;此时测试公网能否正常访问&#xff0c;如能正常访问&#xff0c;则继续往下。 二、…

入职字节两个月,实在卷不动,还是离职了

对自己收入不满意&#xff0c;就看下自己每天做了什么&#xff0c;把每天记录下来&#xff0c;看下自己的时间都用在哪里了。 对自己的时间分配搞清楚了&#xff0c;就可以着手去改进&#xff0c;如果一直糊涂的过&#xff0c;时间到了报复就来了。 时间管理很简单&#xff0c…

零代码、一键生成、低成本,深兰科技硅基大脑SaaS平台国内首发

在6月20日举行的2023中国(苏州)独角兽企业大会上&#xff0c;深兰科技创始人、董事长陈海波在主旨演讲中宣布&#xff0c;深兰科技推出“深兰科技硅基大脑SaaS平台”&#xff0c;旨在为个人和企业提供更便捷、更全面的大语言模型智能化应用。 AI大模型驱动“智慧涌现”&#xf…

Redis实战篇(一)

Redis实战篇基于哔哩哔哩中黑马程序员的黑马点评项目 一.缓存 1.1 缓存概述 1.什么是缓存 缓存&#xff0c;就是数据交换的缓冲区&#xff0c;俗称的缓存就是缓冲区内的数据&#xff0c;一般从数据库中获取&#xff0c;存储于本地代码 2.为什么要使用缓存 缓存数据存储于…

性能测试-压测问题分析,生产环境性能压测优化(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 项目背景&#xf…

从0到1精通自动化测试,pytest自动化测试框架,allure2生成html报告(史上最详细)(九)

一、前言 allure是一个report框架,支持java的Junit/testng等框架,当然也可以支持python的pytest框架&#xff0c;也可以集成到Jenkins上展示高大上的报告界面。 环境准备&#xff1a; python3.6windows环境pycharmpytest-allure-adaptorallure2.7.0java1.8 二、pytest-allu…

在 Blender 和 3DCoat 中创建风格化的幻想屋

今天云渲染小编给大家带来的是CG艺术家Brian Nguyen 最近的项目“一个风格化的幻想屋”幕后制作&#xff0c;讨论了 Blender 中的建模过程和 3DCoat 中的纹理过程&#xff0c;并详细介绍了如何设置灯光和K动画。 介绍 我是 Brian Nguyen&#xff0c;程式化的 3D 艺术家&#…

阿里云EMAS超级App助力Agmo电动车超级应用程序发布

近日&#xff0c;阿里云宣布与马来西亚本土数字方案专家Agmo控股&#xff08;Agmo Holdings Berhad&#xff0c;简称Agmo&#xff09;展开合作&#xff0c;签署谅解备忘录&#xff0c;联手推出马来西亚首个Agmo电动车超级应用程序。此次合作也标志着阿里云在中国以外的市场首次…

电商营销小程序优势有哪些?电商营销小程序功能推荐

随着流量成本越来越高、流量逐渐被平台所垄断&#xff0c;因此转化公域流量至私域成为越来越多企业的选择&#xff0c;对于电商营销而言&#xff0c;电商营销小程序也是一种较为轻便的私域沉淀工具。与传统的电商模式相比&#xff0c;电商营销小程序具有以下几大优势&#xff1…

【嵌入式】MIMXRT685SFVKB 32位微控制器、5CGXFC4C7F27C8N FPGA现场可编程门阵列

MIMXRT685SFVKB i.MX RT600交叉MCU是双核微控制器&#xff0c;设有32位Cortex-M33和Xtensa HiFi4音频DSP CPU。i.MX RT600 MCU是NXP EdgeVerse™边缘计算平台的一部分。Cortex-M33 CPU配有两个硬件协处理器&#xff0c;为一系列复杂算法提供增强性能。 核心处理器&#xff1a;A…

【博客671】prometheus如何选择数据点以及处理counter跳变

prometheus如何选择数据点以及处理counter跳变 1、prometheus如何选择数据点 时间是怎么进来的&#xff1f;范围和即时查询&#xff01; 您可能已经注意到&#xff0c;PromQL 查询中对时间的唯一引用是相对引用&#xff08;例如[5m]&#xff0c;回顾 5 分钟&#xff09;。那…