【Java开发】 Spring 10 :Spring Boot 自动配置(Auto Configuration)原理及手动实现

news2024/11/18 19:35:22

用了这么久的 SpringBoot ,我们再来回顾一下它,本文介绍 Spring Boot 的自动配置,这是它区别于 Spring 的最大的点,本文的自动配置项目包含三个项目,建议拉取仓库里的代码进行实践:尹煜 / AutoConfigDemo · GitCode

 目录

1 Auto Configuration 介绍

2 自动配置原理

2.1 启动类注解

① @SpringBootApplication

② @SpringBootConfiguration

② @EnableAutoConfiguration

③ @ComponentScan(包扫描)

2.2 条件类注解

① 类条件

② 属性条件

③ Bean 条件

④ 资源条件

⑤ Web 应用条件

⑥ 其他条件

3 手动实现自动配置

3.1 资源配置

3.2 项目代码展示

① 主项目(autoconfigure-demo)

② 副项目(geektime-spring-boot-autoconfigure)

③ 副项目(greeting)

3.3 自动/手动配置实现

① 自动配置

② 手动配置

③ 关闭配置


1 Auto Configuration 介绍

Spring Boot 目的在于简化 Spring 繁琐的 XML 配置,本质依然还是Spring框架,使用 Spring Boot后可不再使用任何 XML 配置来启动一个服务,使得使用微服务架构时可以更加快速的建立一个应用。

Spring Boot 特点:

  • 提供了固定的配置来简化配置,即约定大于配置
  • 尽可能地自动配置 Spring 和第三方库,即能自动装配
  • 内嵌容器,创建独立的 Spring 应用
  • 让测试变的简单,内置了JUnit、Spring Boot Test等多种测试框架,方便测试
  • 提供可用于生产的特性,如度量、运行状况检查和外部化配置
  • 完全不需要生成代码,也不需要 XML 配置

2 自动配置原理

首先引用一张 Spring Boot 的自动配置原理流程图 👇

2.1 启动类注解

① @SpringBootApplication

这是 Spring Boot 启动类的注解,也是自动配置的开始 👇

@SpringBootApplication
public class SpringbootWorkApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootWorkApplication.class, args);
    }
}

@SpringBootApplication 用于标注在 SpringBoot 的主配置类上,也就是说项目应该运行该类的 main 方法来启动 SpringBoot 项目;其本质是一个组合注解,进入该注释的代码 👇

@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 {

查看该类的元信息,其实主要包含以下注解:

② @SpringBootConfiguration

查看一下它的源码 👇

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}
  • @Target用于定义注解的使用位置,如果没有该项,表示注解可以用于任何地方。ElementType.TYPE 表示类,接口或者枚举。
  • @Retention用于指明修饰的注解的生存周期,即会保留到哪个阶段。RUNTIME 表示运行级别保留,编译后的class文件中存在,在jvm运行时保留,可以被反射调用。
  • @Documented指明修饰的注解,可以被例如 javadoc 此类的工具文档化,只负责标记,没有成员取值。
  • @Configuration表明这个一个 Spring Boot 的配置类,可以向容器中注入组件。

② @EnableAutoConfiguration

顾名思义,该注释含义是启动自动配置~

查看它的源码:

@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 {};
}
  • @Inherited:用于标注一个父类的注解是否可以被子类继承,如果一个注解需要被其子类所继承,则在声明时直接使用@Inherited注解即可。如果没有写此注解,则无法被子类继承。
  • @AutoConfigurationPackage:自动导入配置包
  • @Import({AutoConfigurationImportSelector.class}):开启自动配置类的导包的选择器,即是导入哪些类,有选择性的导入。

③ @ComponentScan(包扫描)

  • 该注释用于 Configuration 类的组件扫描指令
  • 提供与 Spring XML 的 context:component-scan 元素并行的支持
  • 可以通过 basePackageClasses 或 basePackages 来定义要扫描的特定包, 如果没有定义特定的包,将从声明该注解的类的包开始扫描
  • 也可在启动类做如下配置,用于排除某些特定类包的扫描
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,
        DataSourceTransactionManagerAutoConfiguration.class,
        JdbcTemplateAutoConfiguration.class})
public class DataSourceDemoApplication implements CommandLineRunner {
    public static void main(String[] args) {
        SpringApplication.run(DataSourceDemoApplication.class, args);
    }
}

2.2 条件类注解

@Conditional :所有注解都基于它,是spring4引入的注解

后边的项目也将使用条件类注释来测试自动配置和手动配置~

① 类条件

  • @ConditionalOnClass:当存在一个类时,如何做
  • @ConditionalOnMissingClass:当不存在一个类时,如何做

② 属性条件

  • @ConditionalOnProperty特定的属性为什么时,做什么,如果不存在,赋给他一个默认的值

③ Bean 条件

  • @ConditionalOnBean:存在某个bean时,如何做
  • @ConditionalOnMissingBean:不存在某个bean时,如何做
  • @ConditionalOnSingleCandidate:Spring容器中是否存在且只存在一个对应的实例。只有3个属性value、type、search,跟ConditionalOnBean中的这3种属性值意义一样

④ 资源条件

  • @ConditionalOnResource:是否存在指定的资源文件。只有一个属性 resources,是个 String 数组,会从类加载器中去查询对应的资源文件是否存在

⑤ Web 应用条件

  • @ConditionalOnWebApplication:如果是web应用,如何做
  • @ConditionalOnNotWebApplication:如果不是web应用,如何做

⑥ 其他条件

  • @ConditionalOnExpression sper:表达式计算结果是怎么样的
  • @ConditionalOnJava:在特定的java版本上能做哪些事情
  • @ConditionalOnJndi Jndi:条件是如何的

3 手动实现自动配置

以下资源来自《玩转 Spring 全家桶》课程,项目源码:尹煜 / AutoConfigDemo · GitCode

3.1 资源配置

首先需要将三个项目通过 IDEA 合并在一起,主项目是 autoconfigure-demo:

首先通过 IDEA 打开主项目,然后按照下述流程逐一导入两外两个项目即可 👇

3.2 项目代码展示

项目源码:尹煜 / AutoConfigDemo · GitCode

因涉及到三个项目,且彼此呈依赖关系,因此会有点难以理解,本文希望尽可能讲得清楚一些~

① 主项目(autoconfigure-demo)

路径:geektime/spring/hello/AutoconfigureDemoApplication.java

@SpringBootApplication
public class AutoconfigureDemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(AutoconfigureDemoApplication.class, args);
	}

//	@Bean
//	public GreetingApplicationRunner greetingApplicationRunner() {
//		return new GreetingApplicationRunner("Spring!");
//	}
}

路径:src/main/resources/application.properties

greeting.enabled=true 用于搭配 @ConditionalOnProperty 的条件判断,见于第2个项目的启动类

greeting.enabled=true

路径:autoconfigure-demo\pom.xml

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.3.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>geektime.spring.hello</groupId>
	<artifactId>autoconfigure-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>autoconfigure-demo</name>
	<description>Demo project for Spring Boot</description>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

		<!-- 以下是手工实现的自动配置 -->
		<dependency>
			<groupId>geektime.spring.hello</groupId>
			<artifactId>geektime-spring-boot-autoconfigure</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>

		<!-- 以下是基于Spring Boot的自动配置 -->
		<dependency>
			<groupId>geektime.spring.hello</groupId>
			<artifactId>greeting</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

② 副项目(geektime-spring-boot-autoconfigure)

路径:geektime/spring/hello/greeting/GreetingAutoConfiguration.java

@Configuration
@ConditionalOnClass(GreetingApplicationRunner.class)//当classpath中 存在GreetingApplicationRunner时 才能生效
public class GreetingAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean(GreetingApplicationRunner.class)//不存在GreetingApplicationRunner这个类型的bean的时候,才能生效
    @ConditionalOnProperty(name = "greeting.enabled", havingValue = "true", matchIfMissing = true)//greeting.enabled这个属性为true 才能执行,如果没有配置这个值,就默认值为true
    public GreetingApplicationRunner greetingApplicationRunner() {
        return new GreetingApplicationRunner();
    }
}

路径:src/main/resources/META-INF/spring.factories

模拟 Spring Boot 的自动配置(自动配置时,Spring Boot 会扫描到类路径下的META-INF/spring.factories 配置文件,把 EnableAutoConfiguration 对应的的 Bean 值添加到容器中)

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
geektime.spring.hello.greeting.GreetingAutoConfiguration

路径:geektime-spring-boot-autoconfigure\pom.xml

该副项目也依赖 greeting 项目

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-dependencies</artifactId>
				<version>${spring-boot.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-autoconfigure</artifactId>
		</dependency>
		<dependency>
			<groupId>geektime.spring.hello</groupId>
			<artifactId>greeting</artifactId>
			<version>0.0.1-SNAPSHOT</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>

③ 副项目(greeting)

路径:geektime/spring/hello/greeting/GreetingApplicationRunner.java

用于检验自动配置成果!

@Slf4j
public class GreetingApplicationRunner implements ApplicationRunner {

    private String name;

    public GreetingApplicationRunner(){
        this("OK!");
    }

    public GreetingApplicationRunner(String name) {
        this.name = name;
        log.info("Initializing GreetingApplicationRunner.{}",name);
    }

    public void run(ApplicationArguments args) throws Exception {
        log.info("Hello everyone! We all like Spring! ");
    }
}

路径:greeting\pom.xml

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-dependencies</artifactId>
				<version>${spring-boot.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
		</dependency>
	</dependencies>

以上,所有文件展示完毕~

3.3 自动/手动配置实现

① 自动配置

Ⅰ 注解主项目(autoconfigure-demo)中 AutoconfigureDemoApplication 启动类中的 bean

Ⅱ application.properties 中 greeting.enabled=true

启动主项目的启动类,日志输出显示自动配置成功,使用的是使用空参构造的配置--自动配置 👇

② 手动配置

Ⅰ 启动主项目(autoconfigure-demo)中 AutoconfigureDemoApplication 启动类中的 bean 👇

Ⅱ application.properties 中 greeting.enabled=true

启动主项目的启动类,日志输出显示手动配置成功,使用的是有参构造--手动配置 👇

③ 关闭配置

Ⅰ 注解主项目(autoconfigure-demo)中 AutoconfigureDemoApplication 启动类中的 bean

Ⅱ application.properties 中 greeting.enabled=false

启动主项目的启动类,日志输出显示该 bean 未被创建--关闭配置 👇


参考文章

SpringBoot自动配置原理_程序员老石的博客-CSDN博客_springboot自动配置原理

动手实现自己的自动配置_L# S@的博客-CSDN博客

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

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

相关文章

SOFA Weekly|MOSN v1.3.0 版本发布、公众号半自助投稿、本周 Contributor QA

SOFA WEEKLY | 每周精选 筛选每周精华问答&#xff0c;同步开源进展欢迎留言互动&#xff5e;SOFAStack&#xff08;Scalable Open Financial Architecture Stack&#xff09;是蚂蚁集团自主研发的金融级云原生架构&#xff0c;包含了构建金融级云原生架构所需的各个组件&#…

不接受反驳,性能最强,功能最强的Java日志框架

Logback 算是JAVA 里一个老牌的日志框架&#xff0c;从06年开始第一个版本&#xff0c;迭代至今也十几年了。不过logback最近一个稳定版本还停留在 2017 年&#xff0c;好几年都没有更新&#xff1b;logback的兄弟 slf4j 最近一个稳定版也是2017年&#xff0c;有点凉凉的意思。…

tep支持pytest-xdist分布式执行用例及合并Allure报告

tep近期更新频率较快&#xff0c;一方面是作者在积极投入到tep工具开发中&#xff1b;另一方面是我们聚集了20位小伙伴&#xff0c;一起合力打造EasyPytest测试平台&#xff0c;teprunner的FastAPI升级版本&#xff0c;依托于tep&#xff0c;帮你高效管理pytest测试用例。陆续也…

使用OpenGPT(ChatGPT)搭建 QQ 机器人

本教程来自&#xff1a;OpenGPT搭建QQ机器人-憨憨博客 有问题可来我博客询问&#xff1a;我的博客 准备 一个服务器&#xff1a;Windos&#xff0c;Centos&#xff0c;Ubuntu 环境&#xff1a;Python 一个 QQ 号用作机器人 一个 OpenAI 账号 (注册教程自行搜索) 搭建 这里我用…

Java最流行的Spring框架该怎么学?阿里、腾讯、字节跳动等大厂面试中关于Spring都会问什么?

Spring作为现在最流行Java 开发技术&#xff0c;其内部源码设计非常优秀。如果你不会Spring&#xff0c;那么很可能面试官会让你回家等通知。 Spring是什么&#xff1f; 有一个工地&#xff0c;几百号人在用铁锹铲子挖坑。 如果开一辆挖掘机来&#xff0c;用一天时间干的活就…

【数据结构与算法】图

目录 一、图的基本概念 二、图的存储结构 1、邻接矩阵 2、邻接表 三、图的遍历 1、DFS 2、BFS 四、最小生成树 1、Kruskal算法 2、Prim算法 五、最短路径问题 1、Dijkstra 2、Bellman-Ford 3、Floyd-Warshall 总结 一、图的基本概念 图是由顶点集合及顶点间的关…

tslearn学习:快速入门

文章目录前言一、安装二、时间序列格式2.1 格式化时间序列2.2 读取标准数据集三、机器学习算法3.1 分类3.2 回归3.3 最近邻搜索3.4 聚类前言 tslearn快速入门学习。官网&#xff1a;tslearn quick-start 一、安装 采用pip install安装tslearn库 pip install tslearn二、时间…

基于C#制作一个音乐播放器

此文主要基于C#制作音乐播放器&#xff0c;可实现导入本地歌曲、音乐播放、音量设置、歌词显示等。 实现流程1.1、创建项目1.2、准备素材1.3、功能开发实现流程 1.1、创建项目 打开Visual Studio&#xff0c;右侧选择创建新项目。 搜索框输入winform&#xff0c;选择windows窗…

测控一体化闸门 灌区智能控制闸门 渠道智能测控闸门系统解决方案

平升电子测控一体化闸门系统/灌区智能控制闸门/渠道智能测控闸门系统解决方案集闸门远程/自动控制、渠道水位流量监测、远程通信、图像/视频监控等功能于一体&#xff0c;具备多种闸门启闭控制方式和多种流量计量方式&#xff0c;应用于支渠、斗渠、农渠的精准用水控制与计量。…

数据库原理及MySQL应用 | 程序流程控制

解决复杂问题不可能通过一个SQL语句完成&#xff0c;我们需要执行多个SQL操作。流程控制语句的作用就是控制存储过程或存储函数中SQL语句的执行顺序&#xff0c;是我们完成复杂操作必不可少的一部分。 流程控制语句是指可以控制程序运行顺序的语句&#xff0c;程序运行顺序主要…

各种数据类型的SPI, UART, I2C等方式的通信传输以及存储到EEPROM、Flash等设备的简易实现方法

各种类型的数据传输和存储就涉及到大小端的问题&#xff0c;首先要简单说下芯片的大小端问题&#xff0c;我们这里主要讨论Cortex-M内核。 M内核支持大端或者小端&#xff0c;实际应用中大部分内核都是小端。以STM32为例&#xff0c;全部都是小端&#xff0c;而且是芯片设计之…

Spring Cloud Alibaba Nacos Config - - - >多配置文件/共享配置

源码地址(重点开源码中的 nacos8030 模块)&#xff1a;https://download.csdn.net/download/weixin_42950079/87264006 多配置文件 / 共享配置 在一个微服务架构应用系统中可能包含成百上千个微服务。而很多微服务可能都引入相同的中间件&#xff0c;当环境中引入的中间件较多时…

【eth uniswap】uniswap 自动路径(Auto Router)错误导致的swap超大损耗

____tz_zs 2022-06-09 稿 对于同时有v2池子和v3池子的Token&#xff0c;感觉最近uniswap的app的自动路由寻址&#xff08;Auto Router&#xff09;有点问题&#xff0c;找的永远是v3的&#xff08;如示例caw/weth&#xff09;池子。此时的情况是v3池子很小&#xff0c;只有几十…

用 HarmonyOS ArkUI 来开发一个健康饮食应用

本文演示如果在DevEco Studio 3里面&#xff0c;用HarmonyOS的ArkUI来开发一个健康饮食应用。体验HarmonyOS 3最新API 9&#xff01; 获取HarmonyOS应用 HarmonyOS的ArkUI来开发一个健康饮食的ArkUI程序“ArkUIHealthyDiet”&#xff0c;基础代码已经有了[1]&#xff0c;个人…

【数据结构初阶】八大排序算法+时空复杂度

学会控制自己是人生的必修课 文章目录一、插入排序1.直接插入排序2.希尔排序二、选择排序1.直接选择排序2.堆排序&#xff08;已经建好堆的基础之上&#xff09;三、交换排序&#xff08;Swap&#xff09;1.冒泡排序&#xff08;大学牲最熟悉的排序&#xff09;2.快速排序&…

Python 数据库开发实战-Mac系统下通过homebrew安装Redis数据库

此文章的前置条件是 “Mac系统已安装过Homebrew”&#xff0c;如果未安装&#xff0c;可访问 “Mac 安装 homebrew 详细教程” 一文&#xff0c;详细介绍Homebrew的用法。利用 “Homebrew” 对 “Redis” 进行安装管理&#xff0c;那是一个方便啊。 利用 homebrew 安装 Redis …

【Windows逆向】【Qt】日志信息打印

▒ 目录 ▒&#x1f6eb; 导读需求开发环境1️⃣ 示例程序Demo2️⃣ 编写功能&#xff08;QtCreator版本&#xff09;3️⃣ 编写功能&#xff08;VS版本&#xff09;&#x1f6ec; 文章小结&#x1f4d6; 参考资料&#x1f6eb; 导读 需求 调试是编程中常见的定位手段&#xf…

字节一面,操作系统这题没答好,可惜了

问题引入&#xff1a; 在曾经我们学习Linux的经历中&#xff0c;我们也是多次使用信号的。比如&#xff1a;当我们在使用xshell时&#xff0c;在命令行中按Ctrlc&#xff0c;这个键盘输入产生了一个硬件中断&#xff0c;被操作系统获取&#xff0c;解释成信号&#xff0c;发送…

两百行C++代码实现yolov5车辆计数部署(通俗易懂版)

这周用opencv简单实现了一下基于yolov5检测器的车辆计数功能&#xff0c;方法是撞线计数&#xff0c;代码很简单一共就两百多行&#xff0c;测试视频是在b站随便下载的。注&#xff1a;该代码只能演示视频demo效果&#xff0c;一些功能未完善&#xff0c;离实际工程应用还有距离…

JMeter整体综述

JMeter整体综述1. JMeter体系结构及运行原理1.1 主要的组件1.2 运行原理2. 元件执行顺序和作用域2.1 元件执行顺序2.2 元件执行作用域3. 参考1. JMeter体系结构及运行原理 负载模拟&#xff1a;负责模拟用户请求。如取样器有参数化的需求&#xff0c;可通过配置元件或前置处理器…