@SpringBootApplication自动配置原理剖析

@SpringBootApplication自动配置原理剖析

自动配置: 根据我们添加的依赖,会自动将一些配置类的bean注册进ioc容器中,可以使用@Autowired或者@Resource等注解来使用它。

1.1 SpringBootApplication

Spring Boot项目创建完成会默认生成一个Application的入口类(启动类),命名规则artifactId(项目名)+Application
在这里插入图片描述

@SpringBootConfiguration //标明该类为配置类
@EnableAutoConfiguration //启动自动配置功能
@ComponentScan( //启动包扫描 
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)

@SpringBootApplication是一个"三体"结构,实际上它是一个复合Annotation(注解):

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;

//这个注解表示 @SpringBootApplication 只能用于类级别的元素(即 ElementType.TYPE)。
@Target({ElementType.TYPE}) //注解的适用范围,Type表示注解可以描述在类,接口,注解或枚举中

//这个注解表示 @SpringBootApplication 在运行时仍然有效,因此可以通过反射机制访问到。
@Retention(RetentionPolicy.RUNTIME) //表示注解的生命周期,Runtime运行时

//这个注解表示 @SpringBootApplication 的相关信息会被包含在 Java 文档中。
@Documented 

//这个注解表示如果一个父类被 @SpringBootApplication 注解,那么它的子类也会继承这个注解。
@Inherited 

//这个注解等同于 @Configuration 注解,表示这是一个配置类,可以被用来声明 Bean 对象并装配到 Spring 容器中。在这个上下文中,它指示当前类是一个 Spring 配置类。
@SpringBootConfiguration //标明该类为配置类

//这个注解是 Spring Boot 自动配置的核心。当应用启动时,Spring Boot 会根据 classpath 中存在的库(JAR 文件)来自动配置 Bean。
@EnableAutoConfiguration //启动自动配置功能

//这个注解用于启用组件扫描,以便发现和管理带有特定注解的类(如 @Component、@Service、@Repository 和 @Controller 等)。如果没有指定扫描的包路径,那么默认会扫描包含 @SpringBootApplication 注解的类所在的包及其子包。
@ComponentScan( //启动包扫描 
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
//通过使用 @SpringBootApplication 注解,开发者可以在单一的类中定义整个应用程序所需的配置,而不需要在 XML 或 Java 配置文件中进行大量的手动配置。这正是 Spring Boot 的核心理念之一:约定优于配置。
public @interface SpringBootApplication {

    //根据class来排除特定的自动配置类,使其不能加入spring容器,传入参数value类型是class类型
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    //这个方法是 @EnableAutoConfiguration 注解的一个别名,用于指定要排除的自动配置类。
    Class<?>[] exclude() default {};

    //根据classname 来排除特定的类,使其不能加入spring容器,传入参数value类型是class的全类名字符串数组
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    //这个方法也是 @EnableAutoConfiguration 注解的一个别名,用于指定要排除的自动配置类的名称。
    String[] excludeName() default {};

    //指定扫描包,参数是包名的字符串数组,用于激活@Component(元件)等注解类的初始化
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    //这个方法是 @ComponentScan 注解的一个别名,用于指定要扫描的基础包。
    String[] scanBasePackages() default {};

    //扫描特定的包,参数类是Class类型数组
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    //这个方法也是 @ComponentScan 注解的一个别名,用于指定要扫描的基础包中的类。
    Class<?>[] scanBasePackageClasses() default {};

    //bean名称生成器,用于命名被扫描到并注册成spring容器中的bean的bean名称
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "nameGenerator"
    )
    //这个方法是 @ComponentScan 注解的一个别名,用于指定 Bean 名称生成器。
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    /**
     * 注解的意思是proxyBeanMethods配置类是用来指定 @Bean 注解标注的方法是否使用代理,默认是 true 使用代理
     * (proxyBeanMethods = true)[保证每个@Bean方法被调用多少次返回的组件都是新创建的]
     */
    @AliasFor(
        annotation = Configuration.class
    )
    //这个方法是 @Configuration 注解的一个别名,用于指定是否代理 bean 方法。默认为 true,表示会代理。
    boolean proxyBeanMethods() default true;
}

@AliasFor注解,该注解通常用于桥接到其他注解,该注解的属性中指定了所桥接的注解类,@SpringBootApplication定义的属性在"三体"组合注解中已经定义过了,之所以使用@AliasFor注解并重新在@SpringBootApplication中进行定义,更多的是为了减少用户使用多注解带来的麻烦

Full全模式,Lite轻量级模式
 boolean proxyBeanMethods() default true;
  • Full(proxyBeanMethods = true): proxyBeanMethods参数设置为true时即为:Full全模式。该模式下注入容器中的同一个组件无论被取出多少次都是同一个bean实例,即单实例对象,在该模式下Spring Boot每次启动都会判断检查容器中是否存在该组件。
  • Lite(proxyBeanMethods = false) proxyBeanMethods参数设置为false时即为:Lite轻量级模式。该模式下注入容器中的同一个组件无论被取出多少次都是不同的bean实例,即多实例对象,在该模式下Spring Boot每次启动会跳过检查容器中是否存在该组件。

什么时候用Full全模式,什么时候用Lite轻量级模式?

  • 当在你的同一个Configuration配置类中,注入到容器中的bean实例之间有依赖关系时,建议使用Full全模式
  • 当在你的同一个Configuration配置类中,注入到容器中的bean实例之间没有依赖关系时,建议使用Lite轻量级模式,以提高Spring Boot的启动速度和性能

1.2 @ComponentScan

@ComponentScan 注解用于启动组件扫描,以便发现和管理带有特定注解的类(如 @Component@Service@Repository@Controller 等)。在这个注解中使用了两个 @Filter,它们的作用是排除某些类或包从组件扫描中。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AliasFor;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
    @AliasFor("basePackages")
    String[] value() default {};

    @AliasFor("value")
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;

    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;

    String resourcePattern() default "**/*.class";

    boolean useDefaultFilters() default true;

    ComponentScan.Filter[] includeFilters() default {};

    ComponentScan.Filter[] excludeFilters() default {};

    boolean lazyInit() default false;

    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public @interface Filter {
        FilterType type() default FilterType.ANNOTATION;

        @AliasFor("classes")
        Class<?>[] value() default {};

        @AliasFor("value")
        Class<?>[] classes() default {};

        String[] pattern() default {};
    }
}

  • 1.TypeExcludeFilter
    这是一个 Spring Boot 内置的过滤器,它会排除所有以 .package-info 结尾的文件。这些文件通常包含一些包级别的 Javadoc 注释信息,而不是实际的类定义。因此,将它们排除在组件扫描之外可以提高扫描效率。

  • 2.AutoConfigurationExcludeFilter
    这个过滤器是 Spring Boot 自动配置的一部分,它会排除那些已经被自动配置处理过的类。因为自动配置已经为这些类创建了相应的 Bean,所以没有必要再通过组件扫描来发现它们。

综上所述,这段代码的意思是:在进行组件扫描时,除了排除以 .package-info 结尾的文件外,还排除那些已经被自动配置处理过的类。这样做的目的是避免重复地创建相同的 Bean,从而优化应用程序的启动速度和运行性能。

1.3 EnableAutoConfiguration

@EnableAutoConfiguration 是 Spring Boot 中的一个核心注解,用于启用自动配置功能。让我们从源码的角度来解释这个注解的工作原理:
1.定义:
@EnableAutoConfiguration 注解是通过在 Java 类上使用来启用自动配置的。它的定义如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

  1. 组合注解
    @EnableAutoConfiguration 本身是一个复合注解,它包含两个嵌套注解:

    • @AutoConfigurationPackage:这个注解用于创建一个特殊的 Bean,该 Bean 可以确定主配置包的位置,并将所有扫描的基础包添加到同一个逻辑容器中。
    • @Import(AutoConfigurationImportSelector.class):这个注解告诉 Spring 容器要导入一个名为 AutoConfigurationImportSelector 的类。这个类是自动配置的核心实现,负责根据 classpath 中存在的库来决定哪些自动配置类应该被激活。
  2. 自动配置过程
    当你在一个类上使用 @EnableAutoConfiguration 注解时,Spring 容器会读取该注解并执行以下步骤:

    a. 创建一个 AutoConfigurationImportSelector 实例。
    b. 调用 AutoConfigurationImportSelector.selectImports() 方法,该方法会遍历 classpath 中的 META-INF/spring.factories 文件,并查找与 org.springframework.boot.autoconfigure.EnableAutoConfiguration 关联的所有条目。
    c. 对于每个找到的自动配置类,检查是否满足 @Conditional 注解所指定的条件。如果满足条件,则将该自动配置类添加到待导入的列表中。
    d. 将列表中的所有自动配置类注入到 Spring 容器中,这些类通常会定义一些额外的 Bean 来提供所需的功能。

  3. 自定义排除
    @EnableAutoConfiguration 注解允许通过 excludeexcludeName 属性来排除特定的自动配置类。这两个属性分别对应了类名和类的全限定名。

总结起来,@EnableAutoConfiguration 注解通过组合其他注解和利用 AutoConfigurationImportSelector 类实现了 Spring Boot 自动配置的核心功能。它会根据 classpath 中存在的库自动配置所需的 Bean,从而简化应用程序的配置工作。

1.4 逐行解释getAutoConfigurationEntry方法

这段代码是 AutoConfigurationImportSelector 类的 getAutoConfigurationEntry 方法的实现。这个方法负责处理 @EnableAutoConfiguration 注解并确定要导入的自动配置类列表。让我们逐行解释:


 protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    //这一行检查 @EnableAutoConfiguration 注解是否启用。如果注解被禁用,则直接返回一个空的 AutoConfigurationEntry 对象。
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            //这一行从 annotationMetadata 中获取 @EnableAutoConfiguration 注解的所有属性,并将它们封装为一个 AnnotationAttributes 对象。
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);

            //这一行从 META-INF/spring.factories 文件中获取所有与 org.springframework.boot.autoconfigure.EnableAutoConfiguration 关联的条目,这些条目表示了可能需要导入的自动配置类列表。
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

            //这一行移除 configurations 列表中的重复项。
            configurations = this.removeDuplicates(configurations);

            //这一行从 @EnableAutoConfiguration 注解的 exclude 和 excludeName 属性中获取排除的自动配置类列表。
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);

            //这一行检查 exclusions 列表中的类是否存在于 configurations 列表中,如果不包含则抛出异常。
            this.checkExcludedClasses(configurations, exclusions);

            //这一行从 configurations 列表中移除所有出现在 exclusions 列表中的类。
            configurations.removeAll(exclusions);

            //这一行使用 getConfigurationClassFilter() 方法获取一个过滤器,该过滤器用于进一步筛选 configurations 列表中的类。
            configurations = this.getConfigurationClassFilter().filter(configurations);

            //这一行触发一个事件,通知其他监听者有关自动配置导入的信息。
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            
            //最后,返回一个新的 AutoConfigurationImportSelector.AutoConfigurationEntry 对象,其中包含了待导入的自动配置类列表和排除的自动配置类列表。
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

亿欧网首届“元创·灵镜”科技艺术节精彩纷呈,实在智能AI Agent智能体展现硬核科技图景

12月4日-10日&#xff0c;持续一周的首届“元创灵镜”科技艺术节在海南陵水香水湾拉开帷幕&#xff0c;虚实交互创造出的“海岛之镜”开幕式呈现出既真实又虚幻的未来感&#xff0c;融入前沿科技元素的艺术装置作品在“虚实之镜&自然生长”科技艺术展诠释着浪漫想象&#x…

【Axure高保真原型】能增删改的树形表格

今天和大家分享能增删改的树形表格的原型模板&#xff0c;包括展开、折叠、增加、修改、删除表格内容&#xff0c;那这个原型模板是通过中继器制作的&#xff0c;所以使用简单&#xff0c;只需要填写中继器表格&#xff0c;即可自动生成对应的树形表格。这个模板最高支持6级树形…

【python】质数(素数)

质数(又称素数),是指在大于1的自然数中,除了1和它本身外,不能被其他自然数整除(除0以外)的数称之为质数(素数)。比1大但不是质数的数称为合数,1和0既非素数也非合数。 import math #调用math模块 1.判断某一个数是否为质数 import mathdef isPrime(n): #定义一个判断一个数是…

Improving IP Geolocation with Target-Centric IP Graph (Student Abstract)

ABSTRACT 准确的IP地理定位对于位置感知的应用程序是必不可少的。虽然基于以路由器为中心&#xff08;router-centric &#xff09;的IP图的最新进展被认为是前沿的&#xff0c;但一个挑战仍然存在&#xff1a;稀疏IP图的流行&#xff08;14.24%&#xff0c;少于10个节点&…

DockerCompose部署RabbitMQ集群

DockerCompose部署RabbitMQ集群 最近小黄在工作中正好需要部署RabbitMQ集群&#xff0c;借此来记录一下&#xff0c;也希望可以帮助到大家 前置条件 简单介绍一下咱们公司现有的条件以及想要达成的效果 服务器3台&#xff0c;3台都是属于一个专有网络中&#xff0c;也就是说3…

100V耐压 内置MOS ESOP8 40V输入 转5V 2.1A恒压输出

100V耐压内置MOS ESOP8 40V输入转5V 2.1A恒压输出 芯片测试数据如下图&#xff1a;

SAHI强化YOLOv5在小目标上的表现

文章目录 环境前言安装sahiyolov5检测sahi添加新的检测模型 环境 ubuntu 18.04 64bitsahi 0.8.4yolov5 5.0pytorch 1.7.1cu101 前言 目标检测和实例分割是迄今为止计算机视觉中最重要的应用领域&#xff0c;各种目标检测网络层出不穷&#xff0c;然而&#xff0c;小目标的检…

用23种设计模式打造一个cocos creator的游戏框架----(十六)亨元模式

1、模式标准 模式名称&#xff1a;亨元模式 模式分类&#xff1a;结构型 模式意图&#xff1a;运用共享技术有效地支持大量细粒度的对象 结构图&#xff1a; 适用于&#xff1a; 1、一个应用程序使用了大量的对象. 2、完全由于使用大量的对象&#xff0c;造成很大的存储开…

人工智能改变医疗保健:人工智能如何革命医学

人工智能&#xff08;Artificial Intelligence, 简称AI&#xff09;的快速发展正逐渐改变着我们的生活方式和社会结构。在医疗保健领域&#xff0c;AI的应用不仅提供了更准确、高效的诊断和治疗手段&#xff0c;还为医生和患者之间的交流提供了新的途径。本文将探讨人工智能如何…

java springboot+jsoup写一段爬虫脚本 将指定地址的 图片链接 文本 超链接地址存入自己的属性类对象中

首先 还是最基本的 要在 pom.xml 引入依赖 <dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.14.1</version> </dependency>然后 我们可以在项目中创建一个属性类 我这里就叫 WebContent了…

到底什么是DevOps

DevOps不是一组工具&#xff0c;也不是一个特定的岗位。在我看来DevOps更像是一种软件开发文化&#xff0c;一种实现快速交付能力的手段。 DevOps 强调的是高效组织团队之间如何通过自动化的工具协作和沟通来完成软件的生命周期管理&#xff0c;从而更快、更频繁地交付更稳定的…

微信这个地方,收费了

大家好&#xff0c;我是小悟 我们都知道&#xff0c;微信企业类型小程序需要认证&#xff0c;现在微信个人小程序也需要认证了&#xff0c;账号逾期未完成微信认证&#xff0c;将影响账号“被搜索”能力。 这一要求&#xff0c;在很多人看来可能是一项不必要的规定。然而&…

数据结构之---- 图

数据结构之---- 图 什么是图&#xff1f; 图是一种非线性数据结构&#xff0c;由顶点和边组成。我们可以将图 &#x1d43a; 抽象地表示为一组顶点 &#x1d449; 和一组边 &#x1d438; 的集合。 以下示例展示了一个包含 5 个顶点和 7 条边的图。 如果将顶点看作节点&…

【全面解读】洁净室验收及检测设备、方法全介绍

洁净室相关概念 洁净室又可称作无尘室&#xff08;Cleanroom&#xff09;&#xff0c;通常用作专业工业生产或科学研究的一部分&#xff0c;包括制造药品&#xff0c;集成电路&#xff0c;CRT&#xff0c;LCD&#xff0c;OLED和microLED显示器等。洁净室的设计是为了保持极低水…

数据结构之---- 堆、建堆操作、Top‑K 问题

数据结构之---- 堆、建堆操作、Top‑K 问题 什么是堆&#xff1f; 堆是一种满足特定条件的完全二叉树 主要可分为两种类型&#xff1a; 大顶堆&#xff1a;任意节点的值 ≥ 其子节点的值。小顶堆&#xff1a;任意节点的值 ≤ 其子节点的值。 堆作为完全二叉树的一个特例&am…

闲人闲谈PS之四十八——非标定制企业BOM传输问题

惯例闲话&#xff1a;上个月&#xff0c;集团公司组织了一次信息化大会&#xff0c;邀请国内外10几位知名的院士嘉宾来助阵。能听这些站在知识顶端的大佬的讲座&#xff0c;受益匪浅。至少在当下数字化趋势之下&#xff0c;细分领域的发展势头&#xff0c;有了方向性的指点。很…

C++怎么快速提升实力?

C怎么快速提升实力&#xff1f; 在开始前我有一些资料&#xff0c;是我根据自己从业十年经验&#xff0c;熬夜搞了几个通宵&#xff0c;精心整理了一份「C的资料从专业入门到高级教程工具包」&#xff0c;点个关注&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&…

什么是持续测试?为什么要持续测试?

测试是软件开发生命周期 (SDLC) 的重要组成部分。SDLC 的每个阶段都应包含测试&#xff0c;以获得更快的反馈并提高产品质量。 我们来聊聊持续测相关话题&#xff0c;如何从持续测试中获得更大的收益。 何谓持续测试 持续测试有助于在软件开发生命周期 (SDLC) 的所有阶段提供…

本地连锁门店经营可以借助系统实现哪些功能?

不少的连锁门店目前还是很基础的ERPPOS收银&#xff0c;其他的还是走传统的手工管理&#xff0c;大多连锁老板知道借助信息化系统可以帮助门店实现精细化管理&#xff0c;提高运营效率&#xff0c;降低成本&#xff0c;增强竞争力&#xff0c;但不知道怎么去做&#xff0c;能做…

快速搭建知识付费平台?我有才,为你提供一站式解决方案

在当今数字化时代&#xff0c;知识付费已经成为一种趋势&#xff0c;越来越多的人愿意为有价值的知识付费。然而&#xff0c;公共知识付费平台虽然内容丰富&#xff0c;但难以满足个人或企业个性化的需求和品牌打造。同时&#xff0c;开发和维护一个属于自己的知识付费平台需要…