[sping] spring core - 依赖注入

[sping] spring core - 依赖注入

所有代码实现基于 Spring Boot3,core 的概念很宽广,这里的 core concept 主要指的就是 Inversion of Control 和 Dependency Injection,其他的按照进度应该是会被放到其他的 section 记录

之前有写过 IoC 和 DI,不过是基于 XML 实现的,新版课程把所有 XML 相关的部分全都移除掉了,只留下了基于注解的实现,所以这里也会根据更新的课程继续学下去

本篇笔记主要以依赖注入为主,下篇笔记会将其他相关的注解补完

一些 boot 相关的内容在 [spring] spring boot 简述,devtool 配置,actuator 简述,即 properties 配置 有提,包括热更新之类的,这里不多赘述

一些 boot 的注解相关的内容在 [maven] 创建 spring boot 项目及使用 Jenkins 运行 maven 中有提到,这里主要就是学 maven 的时候过了一遍 spring boot 的结构,因为先学的 maven,所以在写 boot 的时候就没重复

Spring Container

在开始讲过 Spring Container 的东西(其实主要就是 Dependency Injection)之前,先捋一下几个概念:

依赖反转原则,Dependence Inversion Principle(DIP)一种原则,可以理解成一种比较高层的指导方针
控制反转,Inversion of Contro(IoC)一种通用术语,比较常见用来形容 DIP 的术语,针对不同的容器实现会不太一样
在 Spring 框架中,两个还满常混用的
依赖注入,Dependency Injection(DI)实现 IoC/DIP 的具体方法
IoC Container具体实现功能的容器,一般可以指代框架,如 Spring Container

虽然 DI 和 IoC 现在两个基本上会被放在一起讲,不过除了 DI 之外,还有一些其他方式也可以实现 IoC:

  • 服务定位器模式,Service Locator Pattern
  • 工厂模式,Factory Pattern
  • 模板方法,Template Method Pattern

基本上说,只要高层代码不控制具体的处理流程,那么这就可以被归类成是一种 IoC

Spring Container 本身内部也实现了 DI 和 Factory Pattern(至少)这两种 IoC 的控制方法,这二者的关系也是相辅相成的,Spring Container 本身通过 bean(可以理解成对象) factory 对对象进行实例化管理,因此才能够内部实现依赖注入

bean 的管理本身包括:

  • 实例化
  • 生命周期管理
  • scope(默认为 singleton)

除了 bean 的管理和 DI 之外,spring container 还负责下面的功能:

  • AOP

    这也是一个比较流行的 term,后面处理到 event handling 再说

  • 事件处理

  • 资源管理

    比如说已经被视为 legacy 的使用 XML 加载内容

    现在也有用 data source 处理相关资源,这个下个阶段也会提到

  • 集成

    其他的 Spring 全家桶

  • 验证、数据转换和格式化

  • spring expression language(SpEL)

    这是 spring 内部使用的一个东西,具体是什么还没了解,先列一下

依赖注入

DI 是一个在顶层不具体使用某个对象,而是在底层通过配置等其他方式确定实例化对象的方式,一个简单的图例为:

在这里插入图片描述

顶层的 app 本身在和 spring 沟通的时候不会声明说需要某个具体的 shape(可能是 abstract class 或是 interface),但是 srping 底层会通过不同的配置获取某个特定实例化的对象返回给 app

下面进入案例部分,我这里创建(cv)了一个新的 spring boot 项目:

在这里插入图片描述

rest controller 这里相当于渲染一个页面,用来输出结果

其实不用创建一个新的 package 也行,不过我 cv 了之前的项目,就……

构造函数注入

这里总共有 3 个步骤:

  1. 创建一个 interface

    package com.example.demo;public interface DBConnection {String connect();
    }

    作为一个简单的案例,这里返回一个 string 即可

  2. 创建一个 Java 类去实现 interface

    package com.example.demo;import org.springframework.stereotype.Component;@Component
    public class JDBC implements DBConnection{@Overridepublic String connect() {return "Connection via JDBC";}
    }

    这里比较特殊的就是一个 @Component 注解

    这个注解会将当前对象作为一个 bean 注册到 spring application context,同时,如果开启 component scan 的话,当前类就会被 spring 自动扫到

  3. 在 RestController 中显示出来

    更新 Rest Controller 的代码:

    package com.example.demo.rest;import com.example.demo.DBConnection;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;@RestController
    public class FirstRestController {private DBConnection dbconn;@Autowiredpublic FirstRestController(DBConnection conn) {dbconn = conn;}@GetMapping("/dbconnection")public String getDBConnection() {return dbconn.connect();}}

    这里 @RestController@Component 的功能类似,不过这里告知 spring 这是一个 web controller,同时可以处理 HTTP 请求

    这里 FirstRestController 的实例化和参数都由 spring 内部进行管理,而控制参数的类型则是由 @Autowired 实现

    当前案例中只有一个 DBConnection 的实例化对象:

    在这里插入图片描述

    因此 spring 会自动注册 JDBC,并将其传到参数中,从而实现依赖注入,即,在某一步,spring 会实现这个代码:

    DBConnection dbconn = new JDBC();FirstRestController firstController = new FirstRestController(dbconn);
    

    最终网页端现实的结果也证明了这点:

    在这里插入图片描述

    ⚠️:从 spring 4.3 以后开始,如果只有一个 constructor 的情况下,@Autowired 是可以省略的

完成了这三步,构造函数注入的案例就实现完毕了

此时项目结构如下:

在这里插入图片描述

setter 注入

构造函数注入是在构造函数上加 @Autowired,setter 注入顾名思义,就是在 setter 函数上加,如:

更新代码如下:

package com.example.demo.rest;import com.example.demo.DBConnection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class FirstRestController {private DBConnection dbconn;@Autowiredpublic void setDBconn(DBConnection dbconn) {System.out.println("dbconn from setter");this.dbconn = dbconn;}@GetMapping("/dbconnection")public String getDBConnection() {return dbconn.connect();}}

效果如下:

在这里插入图片描述

可以看到终端上的输出

同构造函数注入,本质上 spring 在某一个阶段实例化 dbconn 和 rest controller 后,会调用 setter,将对应的 dbconn 传到 setter 中去

构造 vs setter 注入

目前 spring boot 是推荐使用构造注入,尤其是依赖为必须项时

换言之,当依赖为可选(optional)时,可以选择使用 setter 注入

field 注入

spring 团队现在已经不推荐使用,使用 field 注入的代码不太好测试

其实现原理为通过 java 反射(reflection),以上面的代码为例,实现方法为:

@RestController
public class FirstRestController {@Autowiredprivate DBConnection dbconn;// ...
}

Qualifiers & Primary

一直到现在,案例中都只使用了一个 JDBC 实现,不过在日常中,interface 一般会对应多个实现,不过这种情况下 spring 无法辨别应该使用哪个实现时,就会报错:

在这里插入图片描述

这个时候 spring 就会提示,使用 @Qualifier 或者 @Primary,这里使用 @Qualifier 或者 @Primary 的方式,构造函数注入和 setter 的实现方法都是一样的

Qualifiers

实现方式如下:

@RestController
public class FirstRestController {private DBConnection dbconn;@Autowiredpublic void setDBconn(@Qualifier("mongoDB") DBConnection dbconn) {System.out.println("dbconn from setter");this.dbconn = dbconn;}// ...
}

显示效果如下:

在这里插入图片描述

注意这里 Qualifier("mongoDB") 中的值和类名是一样的,不过首字母没有大写

Primary

@Qualifiers 是在调用类使用,@Primary 则是在具体要被调用的类使用,并且,只能有一个实现类能使用 @Primary 注解。如果有多个实现使用了 @Primary 注解,那么 spring 同样会报错

这里移除 setter 中的 @Qualifiers,并且在 MySQL 中使用 @Primary:

@Component
@Primary
public class MySQL implements DBConnection {@Overridepublic String connect() {return "Connection via MySQL";}
}

如:

在这里插入图片描述

之所以要移除 @Qualifiers 的原因是因为 @Qualifiers 的权重高于 @Primary,如果二者同时出现,那么 @Qualifiers 会覆写 @Primary

也因此,一般来说推荐使用 @Qualifiers 而不是 @Primary

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

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

相关文章

MARS: An Instance-aware, Modular and Realistic Simulator for Autonomous Driving

MARS: An Instance-aware, Modular and Realistic Simulator for Autonomous Driving(基于神经辐射场的自动驾驶仿真器)https://github.com/OPEN-AIR-SUN/marshttps://arxiv.org/pdf/2307.15058.pdfhttps://mp.weixin.qq.com/s/6Ion_DZGJwzs8JOoWMMbPw …

Ubuntu基于Docker快速配置GDAL的Python、C++环境

本文介绍在Linux的Ubuntu操作系统中,基于Docker快速配置Python与C 这2种不同编程语言可用的地理数据处理库GDAL开发环境的方法。 本文就将Python与C 这2种不同编程语言的GDAL模块配置方法分开来介绍,大家依据自己的需求来选择即可——但无论是哪种方法&a…

计算机竞赛 目标检测-行人车辆检测流量计数

文章目录 前言1\. 目标检测概况1.1 什么是目标检测?1.2 发展阶段 2\. 行人检测2.1 行人检测简介2.2 行人检测技术难点2.3 行人检测实现效果2.4 关键代码-训练过程 最后 前言 🔥 优质竞赛项目系列,今天要分享的是 行人车辆目标检测计数系统 …

springmvc-JSR303进行服务端校验分组验证SpringMVC定义Restfull接口异常处理流程RestController异常处理

目录& 1. JSR303 2. JSR303中含有的注解 3. spring中使用JSR303进行服务端校验 3.1 导入依赖包 3.2 添加验证规则 3.3 执行校验 4. 分组验证 4.1 定义分组验证规则 4.2 验证时通过参数指定验证规则 4.3 验证信息的显示 5. SpringMVC定义Restfull接口 5.1 增加s…

opentelemetry、grafana、Prometheus、jaeger、victoria-metrics 介绍、关系与使用

Opentelemetry OTEL 是 OpenTelemetry 的简称, 是 CNCF 的一个可观测性项目,旨在提供可观测性领域的标准化方案,解决观测数据的数据模型、采集、处理、导出等的标准化问题,提供与三方 vendor 无关的服务。 OpenTelemetry 是一组标…

postgresql新特性之Merge

postgresql新特性之Merge 创建测试表测试案例 创建测试表 create table cps.public.test(id integer primary key,balance numeric,status varchar(1));测试案例 官网介绍 merge into test t using ( select 1 id,0 balance,Y status) s on(t.id s.id) -- 当匹配上了,statu…

TempleteMethod

TempleteMethod 动机 在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因 (比如框架与应用之间的关系)而无法和任务的整体结构同时实现。如…

嵌入式Linux应用开发-驱动大全-同步与互斥①

嵌入式Linux应用开发-驱动大全-同步与互斥① 第一章 同步与互斥①1.1 内联汇编1.1.1 C语言实现加法1.1.2 使用汇编函数实现加法1.1.3 内联汇编语法1.1.4 编写内联汇编实现加法1.1.5 earlyclobber的例子 1.2 同步与互斥的失败例子1.2.1 失败例子11.2.2 失败例子21.2.3 失败例子3…

使用CrawlSpider爬取全站数据。

CrawpSpider和Spider的区别 CrawlSpider使用基于规则的方式来定义如何跟踪链接和提取数据。它支持定义规则来自动跟踪链接,并可以根据链接的特征来确定如何爬取和提取数据。CrawlSpider可以对多个页面进行同样的操作,所以可以爬取全站的数据。CrawlSpid…

【2023年11月第四版教材】第17章《干系人管理》(合集篇)

第17章《干系人管理》(合集篇) 1 章节内容2 管理基础3 管理过程3.1 管理的过程★★★ (22上44)3.2 管理ITTO汇总★★★ 4 过程1-识别干系人4.1 数据收集★★★4.3数据分析4.4 权力利益方格4.5 数据表现:干系人映射分析…

springmvc中DispatcherServlet关键对象

以下代码为 spring boot 2.7.15 中自带的 spring 5.3.29 RequestMappingInfo 请求方法相关信息封装,对应的信息解析在 RequestMappingHandlerMapping 的 createRequestMappingInfo() 中实现。 对于 RequestMapping 赋值的相关信息进行解析 protected RequestMappi…

零基础Linux_11(进程)进程程序替换+实现简单的shell

目录 1. 进程程序替换 1.1 程序替换原理 1.2 execl 接口 1.3 execv execlp execvp 1.4 exec 调各种程序 1.5 execle 接口 2. 实现简单的shell 2.1 打印提示和获取输入 2.2 拆开输入的命令和选项 2.3 创建进程和程序替换执行命令 2.4 内建命令实现路径切换 2.5 my…

创建GCP service账号并管理权限

列出当前GCP项目的所有service account 我们可以用gcloud 命令 gcloud iam service-accounts list gcloud iam service-accounts list DISPLAY NAME EMAIL DISABLED terraform …

自动混剪多段视频、合并音频、添加文案的技巧分享

在如今的社交媒体时代,视频的重要性越来越被人们所重视。许多人喜欢记录生活中的美好瞬间,并将其制作成视频分享给朋友和家人。然而,对于那些拍摄了大量视频的人来说,一个一个地进行剪辑和合并可能是一项令人头痛的任务。但是&…

1.6 计算机网络的性能

思维导图: 1.6.1 计算机网络的性能指标 前言: 我的理解: 这段前言主要介绍了关于计算机网络性能的两个方面的讨论。首先,计算机网络的性能可以通过一些重要的性能指标来衡量。但除了这些指标之外,还有一些非性能特征…

【计算机网络】因特网中的电子邮件

文章目录 简单邮件传送协议SMTP邮件访问协议POP3IMAPHTTP 参考资料 电子邮件为异步通信媒介 因特网电子邮件系统 电子邮件系统的三个构件:用户代理、邮件服务器、邮件发送和读取协议 用户代理 User Agent 即UA 电子邮件客户端软件,用户与电子邮件系统的接…

代码随想录算法训练营第五十六天 | 动态规划 part 14 | 1143.最长公共子序列、1035.不相交的线、53. 最大子序和(dp)

目录 1143.最长公共子序列思路代码 1035.不相交的线思路代码 53. 最大子序和(dp)思路代码 1143.最长公共子序列 Leetcode 思路 本题和718. 最长重复子数组 区别在于这里不要求是连续的了,但要有相对顺序,即:“ace” …

Moonbeam Ignite强势回归

参与Moonbeam上最新的流动性计划 还记得新一轮的流动性激励计划吗?Moonbeam Ignite社区活动带着超过300万枚GLMR奖励来啦!体验新项目,顺便薅一把GLMR羊毛。 本次Moonbeam Ignite活动的参与项目均为第二批Moonbeam生态系统Grant资助提案中获…

语义分割 Semantic Segmentation

之前了解过语义分割的内容,感觉可以做好多东西,然后就抽空学习了一下,这里记录一下方便以后查阅,这篇文章可能也会随着学习的深入不断更新。 语义分割 Semantic Segmentation 一些基本概念几种语义分割算法Fully Convolutional Ne…

【单片机】16-LCD1602和12864显示器

1.LCD显示器相关背景 1.LCD简介 (1)显示器,常见显示器:电视,电脑 (2)LCD(Liquid Crystal Display),液晶显示器,原理介绍 (3&#xff…