SpringBoot原理(1)--@SpringBootApplication注解使用和原理/SpringBoot的自动配置原理详解

news2024/11/17 17:28:26

文章目录

  • 前言
  • 主启动类的配置
    • @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/687587.html

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

相关文章

WIN10上必不可少的5款优质软件

噔噔噔噔&#xff0c;作为一个黑科技软件爱好者&#xff0c;电脑里肯定是不会缺少这方面的东西&#xff0c;今天的5款优质软件闪亮登场了。 颜色拾取器——ColorPix ​ ColorPix是一个颜色拾取器工具&#xff0c;可以让你快速地获取屏幕上任意位置的颜色值&#xff0c;如RGB、…

ivshmem-plain设备原理分析

文章目录 前言基本原理共享内存协议规范 具体实现设备模型数据结构设备初始化 测试验证方案流程Libvirt配置Qemu配置测试步骤 前言 ivshmem-plain设备是Qemu提供的一种特殊设备&#xff0c;通过这个设备&#xff0c;可以实现虚机内存和主机上其它进程共存共享&#xff0c;应用…

618美妆个护28个榜单:欧莱雅稳住冠军?珀莱雅大爆发第二?

存量时代的购物造节大竞争&#xff0c;作为消费复苏后的首场大促&#xff0c;今年的618堪称史上最“卷”&#xff0c;也承载着消费振兴、经济复苏等希望。 不过&#xff0c;今年所有平台都未公布具体GMV&#xff0c;某种程度说明大促造节的时代俨然已成过去式了。 5月18日&am…

怎么去除视频里的背景音乐?其实非常简单!

如何去除视频背景音乐&#xff1f;在视频处理中&#xff0c;有时我们需要从视频中提取声音并进行处理&#xff0c;而不仅仅是简单地去除整个背景音乐。我们可能需要有选择性地去除人声或背景音乐。这个处理过程对于选用合适的工具至关重要。在本文中&#xff0c;我将分享两种可…

【⑦MySQL】· 一文了解四大子查询

前言 ✨欢迎来到小K的MySQL专栏&#xff0c;本节将为大家带来MySQL标量/单行子查询、列子/表子查询的讲解✨ 目录 前言一、子查询概念二、标量/单行子查询、列子/表子查询三、总结 一、子查询概念 子查询指一个查询语句嵌套在另一个查询语句内部的查询&#xff0c;这个特性从My…

抖音林客生活服务商机构

抖音林客生活服务商机构是在抖音平台上提供各种生活服务的机构或组织。这些机构通常会提供家政服务、保洁服务、美容美发服务等&#xff0c;也有一些提供餐饮、旅游、电商等服务。用户可以通过抖音搜索、浏览和下单&#xff0c;享受到优质的服务体验。 这些服务商机构在抖音…

数值组件滚动趋势图联动需求拆解

技术栈&#xff1a;使用vue3的composition API tsx 进行开发 一、需求描述 直接看UI图吧。 简单描述一下&#xff1a; 数值卡片&#xff1a; 上方部分是一个数值卡片列表&#xff0c;每个卡片维护不同的集中状态&#xff0c;选中态&#xff0c;hover态。 细节&#xff1…

【测试学习】Junit5的简单使用

目录 &#x1f31f;需要知道&#xff1a; &#x1f31f;Junit学习 &#x1f308;1、常用的注解 &#x1f308;2、测试用例的执行顺序 &#x1f308;3、参数化 &#x1f308;4、断言 &#x1f308;5、测试套件 &#x1f31f;需要知道&#xff1a; 问题1&#xff1a;Selen…

ATA-4315高压功率放大器在铁路钢轨损伤检测中的应用

随着高速铁路的建设和不断发展&#xff0c;确保铁路线路的安全和稳定运行变得越来越重要。钢轨作为铁路的重要组成部分&#xff0c;其损坏可能导致严重的事故和交通堵塞。因此&#xff0c;对钢轨损伤进行及时、准确的检测至关重要。高压功率放大器作为一种精密的测试仪器&#…

SQL 优化(一):慎用 SQL 函数

假如有下面这样一张用户表 CREATE TABLE t_user (user_id int(11) NOT NULL AUTO_INCREMENT,username varchar(50) DEFAULT NULL,sex tinyint(1) DEFAULT NULL,mobile varchar(45) DEFAULT NULL,create_time datetime DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (user_id),KEY id…

Linux系统下查看网卡配置和网络流量监控指令:ifconfig、ethtool

文章目录 1 查看/设置网卡&#xff08;ifconfig&#xff09;2 查看网卡配置、属性&#xff08;ip、ethtool&#xff09;3 查看网络接口配置情况&#xff1a;4 查看网络接口属性信息5 测试网路通信&#xff08;ping&#xff09; 6 查看具体网卡配置文件 1 查看/设置网卡&#xf…

Unity | HDRP高清渲染管线学习笔记:Lightmapping(光照烘焙)与Lightmap(光照贴图)

目录 相关概念 1.渐进式光照贴图烘焙 1.1 渐进式光照贴图烘焙对模型的要求 1.2 渐进式光照贴图烘焙对硬件的要求 1.3 渐进式光照贴图烘焙支持的Unity渲染管线 1.4 进行渐进式光照贴图烘焙结果 1.5 渐进式光照贴图烘焙的CPU版本和GPU版本 1.6 Lighting窗口Lightmapping …

2023MWC上海|平行云邀您一起聊聊“未来数字世界中的社交,游戏与娱乐”

“世界移动通信大会&#xff08;MWC上海&#xff09;&#xff0c;自2012年落户上海以来已经举办9届&#xff0c;是中国乃至亚太区域移动产业向世界展示最新发展成果与发展战略的关键平台。2023年MWC上海将于2023年6月28-30日在上海新国际博览中心举行&#xff0c;主题是“时不我…

Spring 更简单的读取和存储对象、使用注解存取对象

文章目录 1.前言2.存储 Bean对象2.1 前置任务&#xff1a;配置扫描路径&#xff08;重中之重&#xff09;2.2 添加注解存储 Bean 对象2.2.1 类注解2.2.2 方法注解 Bean 3.获取 Bean对象3.1 属性注入3.2 构造方法注入3.3 Setter 注⼊3.4 三种注释的优缺点3.5 另⼀种注⼊关键字&a…

python学习——时间序列

目录 1 生成时间1.1 pd.Timestamp1.2 pd.to_datetime 最常用1.2.1 单个转换为时间戳1.2.2 多个时间转换为时间戳1.2.3 日期在前 dayfirst1.2.4 处理特殊格式 format1.2.5 处理无效时间 errors1.2.6 和timestamp互转 1.3 固定跨度时间的生成 pd.date_range 2 时间格式转换 .dt.s…

AI工具在不同领域的应用范围分享,让我们一起了解

随着人工智能技术的不断发展&#xff0c;越来越多的领域开始应用AI工具来解决问题、提高效率和创造力。无论是在医疗、金融、教育还是其他行业&#xff0c;AI工具都展现出了巨大的潜力和价值。今天&#xff0c;我将分享一些常见的AI工具在不同领域的应用范围&#xff0c;让我们…

Consul:什么是Consul? ①

一、思想 微服务、分布式应用的特点就是拆分&#xff0c;拆分带来的问题就是服务器数量成倍增多&#xff0c;两个很重要的原因&#xff0c;需要抽离出来一个项目专门管理这些服务信息。 1、对信息的管理思想&#xff0c;统一化、中心化一直是一套比较常规成熟的标准。 2、并且它…

开源工具系列8:Spring Security

Spring Security 是一套认证授权框架, 支持认证模式如 HTTP BASIC 认证头 (基于 IETF RFC-based 标准), HTTP Digest 认证头 ( IETF RFC-based 标准), Form-based authentication (用于简单的用户界面), OpenID 认证等, Spring Security使得当前系统可以快速集成这些验证机制亦…

Matplotlib---等高线图

1. 等高线图 等高线图&#xff1a;也称水平图&#xff0c;是一种在二维平面上显示 3D 图像的方法。 x np.linspace(-5, 5, 100) y np.linspace(-5, 5, 100)# 将上述数据变成网格数据形式 X, Y np.meshgrid(x, y)# 定义Z与X, Y之间的关系 Z np.sqrt(X**2 Y**2)# 画等高线…

Win11 系统Java17的安装教程:最新版JDK 17.07下载、安装、卸载详解

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…