Spring Boot原理全解析:如何让开发更轻松高效(二)-起步依赖、自动装配

通过这篇博客,读者将能够掌握 Spring Boot 中的配置优先级和 Bean 管理的核心原理,为开发更加高效、可维护的 Spring Boot 应用打下坚实的基础。

目录

前言

起步依赖

自动配置

概述

常见方案

概述

 方案一

 方案二

总结


前言

通过这篇博客,读者将能够掌握 Spring Boot 中的配置优先级和 自动配置的核心原理,为开发更加高效、可维护的 Spring Boot 应用打下坚实的基础。


起步依赖

假如我们没有使用SpringBoot,用的是Spring框架进行web程序的开发,此时我们就需要引入web程序开发所需要的一些依赖。

spring-webmvc依赖:这是Spring框架进行web程序开发所需要的依赖
servlet-api依赖:Servlet基础依赖
jackson-databind依赖:JSON处理工具包
如果要使用AOP,还需要引入aop依赖、aspect依赖
项目中所引入的这些依赖,还需要保证版本匹配,否则就可能会出现版本冲突问题。 

如果我们使用了SpringBoot,就不需要像上面这么繁琐的引入依赖了。我们只需要引入一个依赖就可以了,那就是web开发的起步依赖:springboot-starter-web。 

为什么我们只需要引入一个web开发的起步依赖,web开发所需要的所有的依赖都有了呢?

  • 因为Maven的依赖传递
  • 在SpringBoot给我们提供的这些起步依赖当中,已提供了当前程序开发所需要的所有的常见依赖(官网地址:https://docs.spring.io/spring-boot/docs/2.7.7/reference/htmlsingle/#using.build-systems.starters)。
  • 比如:springboot-starter-web,这是web开发的起步依赖,在web开发的起步依赖当中,就集成了web开发中常见的依赖:json、web、webmvc、tomcat等。我们只需要引入这一个起步依赖,其他的依赖都会自动的通过Maven的依赖传递进来。

结论:起步依赖的原理就是Maven的依赖传递。

自动配置

概述

SpringBoot的自动配置就是当Spring容器启动后,一些配置类、bean对象就自动存入到了IOC容器
中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。

比如:我们要进行事务管理、要进行AOP程序的开发,此时就不需要我们再去手动的声明这些bean对象了,我们直接使用就可以从而大大的简化程序的开发,省去了繁琐的配置操作。

大家会看到有两个CommonConfig,在第一个CommonConfig类中定义了一个bean对象,bean对象的名字叫reader。
在第二个CommonConfig中它的bean名字叫commonConfig,为什么还会有这样一个bean对象呢?原因是在CommonConfig配置类上添加了一个注解@Configuration,而@Configuration底层就是
@Component

所以配置类最终也是SpringIOC容器当中的一个bean对象

在IOC容器中除了我们自己定义的bean以外,还有很多配置类,这些配置类都是SpringBoot在启动的时候加载进来的配置类。这些配置类加载进来之后,它也会生成很多的bean对象。

比如:配置类GsonAutoConfiguration里面有一个bean,bean的名字叫gson,它的类型是
Gson。
com.google.gson.Gson是谷歌包中提供的用来处理JSON格式数据的。

当我们想要使用这些配置类中生成的bean对象时,可以使用@Autowired就自动注入了:

import com.google.gson.Gson;
import com.itheima.pojo.Result;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class AutoConfigurationTests {
    @Autowired
    private Gson gson;
    @Test
    public void testJson(){
        String json = gson.toJson(Result.success());
        System.out.println(json);
     }
}

添加断点,使用debug模式运行测试类程序:

问题:在当前项目中我们并没有声明谷歌提供的Gson这么一个bean对象,然后我们却可以通过@Autowired从Spring容器中注入bean对象,那么这个bean对象怎么来的?

答案:SpringBoot项目在启动时通过自动配置完成了bean对象的创建。

体验了SpringBoot的自动配置了,下面我们就来分析自动配置的原理。其实分析自动配置原理就是来解析在SpringBoot项目中,在引入依赖之后是如何将依赖jar包当中所定义的配置类以及bean加载到SpringIOC容器中

常见方案

概述

解析自动配置的原理就是分
析在 SpringBoot项目当中,我们引入对应的依赖之后,是如何将依赖jar包当中所提供的bean以及
配置类直接加载到当前项目的SpringIOC容器当中的。

接下来,我们就直接通过代码来分析自动配置原理。

准备工作:在Idea中导入"资料\03. 自动配置原理"下的itheima-utils工程

1、在SpringBoot项目 spring-boot-web-config2 工程中,通过坐标引入itheima-utils依赖

@Component
public class TokenParser {
    public void parse(){
        System.out.println("TokenParser ... parse ...");
     }
}

 2、在测试类中,添加测试方法

@SpringBootTest
public class AutoConfigurationTests {
    @Autowired
    private ApplicationContext applicationContext;
    @Test
    public void testTokenParse(){
        System.out.println(applicationContext.getBean(TokenParser.class));
     }
    //省略其他代码...
}

3、执行测试方法

异常信息描述: 没有com.example.TokenParse类型的bean
说明:在Spring容器中没有找到com.example.TokenParse类型的bean对象

思考:引入进来的第三方依赖当中的bean以及配置类为什么没有生效?

  • 原因在我们之前讲解IOC的时候有提到过,在类上添加@Component注解来声明bean对象时,还需要保证@Component注解能被Spring的组件扫描到。
  • SpringBoot项目中的@SpringBootApplication注解,具有包扫描的作用,但是它只会扫描启动类所在的当前包以及子包。
  • 当前包:com.itheima, 第三方依赖中提供的包:com.example(扫描不到)

那么如何解决以上问题的呢?

  • 方案1:@ComponentScan 组件扫描
  • 方案2:@Import 导入(使用@Import导入的类会被Spring加载到IOC容器中)

 方案一

@ComponentScan组件扫描

@SpringBootApplication
@ComponentScan({"com.itheima","com.example"}) //指定要扫描的包
public class SpringbootWebConfig2Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebConfig2Application.class,
        args);
     }
}

重新执行测试方法,控制台日志输出:

大家可以想象一下,如果采用以上这种方式来完成自动配置,那我们进行项目开发时,当需要引入大量的第三方的依赖,就需要在启动类上配置N多要扫描的包,这种方式会很繁琐。而且这种大面积的扫描性能也比较低。
缺点:
1. 使用繁琐
2. 性能低
结论:SpringBoot中并没有采用以上这种方案。

 方案二

@Import导入

  • 导入形式主要有以下几种:

1. 导入普通类
2. 导入配置类
3. 导入ImportSelector接口实现类

1). 使用@Import导入普通类:

@Import(TokenParser.class) //导入的类会被Spring加载到IOC容器中
@SpringBootApplication
public class SpringbootWebConfig2Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebConfig2Application.class,
    args);
     }
}

2)使用@Import导入配置类:
配置类 

@Configuration
public class HeaderConfig {
    @Bean
    public HeaderParser headerParser(){
        return new HeaderParser();
     }
    @Bean
    public HeaderGenerator headerGenerator(){
        return new HeaderGenerator();
     }
}

启动类

@Import(HeaderConfig.class) //导入配置类
@SpringBootApplication
public class SpringbootWebConfig2Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebConfig2Application.class,
        args);
     }
}

 测试类

@SpringBootTest
public class AutoConfigurationTests {
    @Autowired
    private ApplicationContext applicationContext;
    @Test
    public void testHeaderParser(){
        System.out.println(applicationContext.getBean(HeaderParser.class));
     }
    @Test
    public void testHeaderGenerator(){
        System.out.println(applicationContext.getBean(HeaderGenerator.class
        ));
     }
    //省略其他代码...
}

3). 使用@Import导入ImportSelector接口实现类:
ImportSelector接口实现类

public class MyImportSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata
    importingClassMetadata) {
        //返回值字符串数组(数组中封装了全限定名称的类)
        return new String[]{"com.example.HeaderConfig"};
     }
}

启动类

@Import(MyImportSelector.class) //导入ImportSelector接口实现类
@SpringBootApplication
public class SpringbootWebConfig2Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebConfig2Application.class,
        args);
     }
}

我们使用@Import注解通过这三种方式都可以导入第三方依赖中所提供的bean或者是配置类。
思考:如果基于以上方式完成自动配置,当要引入一个第三方依赖时,是不是还要知道第三方依赖中有
哪些配置类和哪些Bean对象?

  • 答案:是的。 (对程序员来讲,很不友好,而且比较繁琐)

思考:当我们要使用第三方依赖,依赖中到底有哪些bean和配置类,谁最清楚?

  • 答案:第三方依赖自身最清楚。

结论:我们不用自己指定要导入哪些bean对象和配置类了,让第三方依赖它自己来指定。

 怎么让第三方依赖自己指定bean对象和配置类?

  • 比较常见的方案就是第三方依赖给我们提供一个注解,这个注解一般都以@EnableXxxx开头的注解,注解中封装的就是@Import注解

4). 使用第三方依赖提供的 @EnableXxxxx注解

第三方依赖中提供的注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)//指定要导入哪些bean对象或配置类
public @interface EnableHeaderConfig {
}

在使用时只需在启动类上加上@EnableXxxxx注解即可

@EnableHeaderConfig //使用第三方依赖提供的Enable开头的注解
@SpringBootApplication
public class SpringbootWebConfig2Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebConfig2Application.class,
        args);
     }
}

以上四种方式都可以完成导入操作,但是第4种方式会更方便更优雅,而这种方式也是SpringBoot当中所采用的方式。


总结

通过本文的讲解,我们希望能够帮助开发者更好地掌握 Spring Boot 配置的工作原理,提升应用的开发效率,并构建出更加稳健和高效的后端系统。

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

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

相关文章

力扣动态规划基础版(矩阵型)

62.不同路径(唯一路径问题) 62. 不同路径https://leetcode.cn/problems/unique-paths/ 方法一:动态规划 找状态转移方程,也就是说它从左上角走到右下角,只能往右或者往下走,那么设置一个位置为&#xff…

Ubuntu 22 安装 Apache Doris 3.0.3 笔记

Ubuntu 22 安装 Apache Doris 3.0.3 笔记 1. 环境准备 Doris 需要 Java 17 作为运行环境,所以首先需要安装 Java 17。 sudo apt-get install openjdk-17-jdk -y sudo update-alternatives --config java在安装 Java 17 后,可以通过 sudo update-alter…

141/142题环形链表

本题返回环入口的位置。使用快慢指针,快指针每次移动两个,慢指针每次移动一个。设前一段距离是a,进入环内到slow和fast相遇的地点距离是b,环内剩下的距离是c,如图所示。 环的长度是bc 慢指针移动距离是ab 快指针移动距离是abk(bc…

leetcode25:k个一组链表反转

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。 k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。 你不能只是单纯的改变节点内部的值…

《今日制造与升级》是什么级别的期刊?是正规期刊吗?能评职称吗?

​问题解答 问:《今日制造与升级》是不是核心期刊? 答:不是,是知网收录的正规学术期刊。 问:《今日制造与升级》级别? 答:国家级。主管单位:中国机械工业联合会 …

【Linux第八课-进程间通信】管道、共享内存、消息队列、信号量、信号、可重入函数、volatile

目录 进程间通信为什么?是什么?怎么办?一般规律具体做法 匿名管道原理代码 命名管道原理代码 system V共享内存消息队列信号量信号量的接口 信号概念为什么?怎么办?准备信号的产生信号的保存概念三张表匹配的操作和系统…

C++builder中的人工智能(18):神经网络中的SoftMax函数

在这篇文章中,我们将探讨SoftMax函数在神经网络中的作用,如何在人工神经网络(ANN)中使用SoftMax函数,以及在AI技术中SoftMax的应用场景。让我们来详细解释这些概念。 SoftMax函数是什么? SoftMax函数是逻辑…

证件照尺寸168宽240高,如何手机自拍更换蓝底

在提供学籍照片及一些社会化考试报名时,会要求我们提供尺寸为168*240像素的电子版证件照,本文将介绍如何使用“报名电子照助手”,借助手机拍照功能完成证件照的拍摄和背景更换,特别是如何将照片尺寸调整为168像素宽和240像素高&am…

pytest+request+allure接口自动化框架搭建分享

介绍分享一个接口自动化框架搭建方法 (pytestrequestallure),这个方案是由 xpcs 同学在TesterHome社区网站的分享。 写在前面 去年11月被裁,到现在还没上岸,gap 半年了。上岸无望,专业技能不能落下,花了两三天时间&…

大数据新视界 -- 大数据大厂之 Impala 性能优化:融合机器学习的未来之路(上 (2-2))(11/30)

💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

Unity 实现数字垂直滚动效果

Unity 实现数字垂直滚动效果 前言项目场景布置Shader代码编写材质球设置代码编写数字图片 前言 遇到一个需要数字垂直滚动模拟老虎机的效果,记录一下。 项目 场景布置 3个Image换上带有RollNumberShader的材质 在RollNumberScript脚本中引用即可 Shader代码编…

[linux]docker基础

常见命令 Docker最常见的命令就是操作镜像、容器的命令,详见官方文档: Docker Docs 案例: 查看DockerHub,拉取Nginx镜像,创建并运行Nginx容器 在DockerHub中搜索Nginx镜像 拉取Nginx镜像 查看本地镜像列表 把镜像保持到本地 查看保持命令的…

纯C++信号槽使用Demo (sigslot 库使用)

sigslot 库与QT的信号槽一样,通过发送信号,触发槽函数,信号槽不是QT的专利,早在2002年国外的一小哥用C写了sigslot 库,简单易用; 该库的官网(喜欢阅读的小伙伴可以仔细研究)&#xf…

(Go语言)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法

0. 序言 从这章开始,在Go基础语法里难度就开始上来了 在学习函数与方法前,先弄明白指针是很重要的。 1. 指针 在没学指针前,相信很多人就已经大概知道指针是个什么东西了。因为它太有名了,当然是与 C和C 的出名有关。 1.1 指针…

基于redis实现API接口访问次数限制

一,概述 日常开发中会有一个常见的需求,需要限制接口在单位时间内的访问次数,比如说某个免费的接口限制单个IP一分钟内只能访问5次。该怎么实现呢,通常大家都会想到用redis,确实通过redis可以实现这个功能&#xff0c…

实在智能受邀出席柳州市智能终端及机器人产业发展合作大会

10 月 27 日至 28 日,由中共柳州市委员会与柳州市人民政府主办的2024柳州市智能终端及机器人产业发展合作大会在柳州莲花山庄隆重举行。大会充分整合各方资源,持续深化与柳州在重大战略规划、重大平台建设、重点产业培育等领域的合作。作为智能体行业的知…

JDBC-PreparedStatement

在前面使用的Statement中,编写sql语句使用的是拼接的形式,这样不仅可读性差,还非常容易导致出错,最大的问题是安全问题。 sql注入 在需要用户输入的地方,用户输入的是SQL语句的片段,最终用户输入的SQL片段…

如何创建备份设备以简化 SQL Server 备份过程?

SQL Server 中的备份设备是什么? 在 SQL Server 中,备份设备是用于存储备份数据的物理或逻辑介质。备份设备可以是文件、设备或其他存储介质。主要类型包括: 文件备份设备:通常是本地文件系统中的一个或多个文件。可以是 .bak 文…

非计算机背景但是想从事医学AI研究,需要掌握的编程语言|个人观点·24-11-08

小罗碎碎念 目前,我们从事医学AI研究的,接触的最多的两种编程语言应该就是R和Python了。那么初学者很容易提出一个疑问,**我想从事医学AI相关的研究的话,应该学哪些编程语言呢?**在文章的开头,我可以先给出…

Jmeter基础篇(21)教你手动修改Jmeter测试报告和压测结果

哈喽呀各位小伙伴!今天给大家带来一期关于Jmeter黑科技的教学! 在日常性能测试过程中,我们经常使用JMeter这个强大的工具来执行压力测试,并通过JMeter的报告生成命令,从CSV或JTL文件中读取数据,生成HTML格式的测试报告。然而,测试报告生成之后,数据就是固定的了,很多…