当前位置: 首页 > news >正文

Spring使用注解进行对象装配(DI)

文章目录

  • 一. 什么是对象装配
  • 二. 三种注入方式
    • 1. 属性注入
    • 2. 构造方法注入
    • 3. Setter注入
  • 三. 三种注入方式的优缺点
  • 四. 综合练习

通过五大类注解可以更便捷的将对象存储到 Spring 中,同样也可以使用注解将已经储存的对象取出来,直接赋值到注解所在类的一个属性中,这一个过程也叫做对象的装配或者叫对象的注入,即 DI。

一. 什么是对象装配

获取 Bean 对象也叫做对象装配,就是把对象取出来放到某个类中,有时候也叫对象注入。
对象装配(对象注入)的实现方法以下 3 种:

  1. 属性注入 ,就是将对象注入到某个类的一个属性当中。
  2. 构造方法注入 ,就是通过构造方法来将对象注入到类中。
  3. Setter 注入 ,通过 SetXXX 系列方法将对象注入到类中。

常见有关对象注入的注解有两个,一个是@Autowired,另外一个是@Resource

🍂它们两者的区别如下:

  1. 出身不同:@Autowired 是由Spring提供的,而 @Resource 是JDK提供的。
  2. 查找顺序不同:从容器中获取对象时 @Autowired 先根据类型再根据名称查询,而 @Resource 先根据名称再根据类型查询。
  3. 使⽤时设置的参数不同:@Resource 支持更多的参数设置(有 7 个),如nametype等,而@Autowired只支持设置required一个参数,用来设置注入 Bean 的时候该 Bean 是否必须存在(true/false)。 imgimg
  4. 依赖注入支持不同:@Autowired 支持属性注入,构造方法注入和 Setter 注入,而 @Resource 只支持属性注入和 Settter 注入,但是不支持构造方法注入。
  5. 对 IDEA 的兼容性支持不同:使用 @Autowired 在 IDEA 旗舰版下可能会有误报(设置required即可);而 @Resource 不存在误报的问题。

二. 三种注入方式

1. 属性注入

属性注入只需要在需要注入对象的属性上加上 @Autowired 或者 @Resource 注解就可以了,这里以 @Autowired 为例。

首先来看第一种情况,待注入的同类对象只有一个,此时我们直接使用 @Autowired 注解就好,不必设置参数,例如我们在UserController类里面注入UserService对象。

下面UserService的结构,先使用 @Service 将 Bean 存放到 Spring 中:

package com.tr.demo.service;import org.springframework.stereotype.Service;@Service
public class UserService {public void sayHi() {System.out.println("Hello, UserService~");}
}

属性注入:

package com.tr.demo.controller;import com.tr.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller
public class UserController {//属性注入@Autowiredprivate UserService userService;public void sayHi() {userService.sayHi();}
}

此时我们就可以在启动类中,使用上下文对象来获取到UserController对象,通过执行UserController对象的sayHi方法来进而调用到注入的UserService对象中的sayHi方法了,此时的UserService对象就不是我们自己new出来的了。

import com.tr.demo.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");UserController usercontroller =  context.getBean("userController", UserController.class);usercontroller.sayHi();}
}

运行结果:
img
上面说的是同类对象只有一个的情况,而如果存在多个同类对象,我们就得通过参数来告知容器我们要注入哪一个对象,不告知就会报错。

比如我们将多个User对象添加到容器中,如下:

package com.tr.demo.model;
// User 结构
public class User {private int id;private String name;@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}package com.tr.demo.model;import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;@Component
public class UserBeans {// 使用方法注解添加多个 User 对象到容器中@Bean("user1")public User user1(){User user = new User();user.setId(1);user.setName("张三");return user;}@Bean("user2")public User user2(){User user = new User();user.setId(2);user.setName("李四");return user;}@Bean("user3")public User user3(){User user = new User();user.setId(3);user.setName("王五");return user;}
}

而在UserController2类中需要注入User对象,此时我们运行程序就会报错:

package com.tr.demo.controller;import com.tr.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller
public class UserController2 {@Autowiredprivate User user;public void sayHi() {System.out.println("Hello, " + user);}
}

我们试着以同样的方法来调用sayHi方法:

img
运行结果:

@Autowired 依赖注入流程首先是先根据类型从容器中获取对象,如果只能获取到一个,那么就直接将此对象注入到当前属性上;如果能获取到多个对象,此时会使用 BeanName 进行匹配,而我们添加到 Spring 中的对象是没有一个叫user的,所以程序就报错了。
img

此时就需要我们来告知容器我们需要哪一个具体的 Bean,要获得目标对象主要有下面三种方法:

  • 1️⃣方法1:将属性的变量名设置为你需要的那个BeanName就可以了,后面的构造方法与 Setter 注入同理,将形参名设置成与BeanName相同即可。imgimg
  • 2️⃣方法2:@Autowired 注解与 @Qualifier 注解配合使用,设置 @Qualifier 的value参数为BeanName即可,要注意 @Qualifier 注解不能修饰方法,只能修饰变量。
    imgimg
  • 3️⃣方法3:将 @Autowired 注解替换成 @Resource 注解的,并将它name参数值设置为BeanName即可。 imgimg

2. 构造方法注入

在构造方法加上 @Autowired 注解就可,要注意 @Resource 注解是不支持构造方法注入的,我们就直接演示如何获取取多个同类对象中的其中一个了,还是用上面添加到容器中的多个 User 对象。

方法1:将构造方法形参名设置为user1

package com.tr.demo.controller;import com.tr.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller
public class UserController3 {private User user;@Autowiredpublic UserController3(User user1) {this.user = user1;}public void sayHi() {System.out.println("Hello, " + user);}
}

启动类就不贴代码了,一样的,运行结果如下:
img

方法2:@Autowired 搭配 @Qualifier

package com.tr.demo.controller;import com.tr.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;@Controller
public class UserController4 {private User user;@Autowiredpublic UserController4(@Qualifier(value = "user2") User user) {this.user = user;}public void sayHi() {System.out.println("Hello, " + user);}
}

运行结果:
img
对了,如果一个类中只有一个构造方法,@Autowired 是可以省略的,演示一下:

package com.tr.demo.controller;import com.tr.demo.model.User;
import org.springframework.stereotype.Controller;@Controller
public class UserController5 {private User user;public UserController5(User user3) {this.user = user3;}public void sayHi() {System.out.println("Hello, " + user);}
}

此时仍然是可以成功注入对象。
img
如果有多个构造方法,要注意此时是不能省略 @Autowired 的,会导致会注入对象失败。

package com.tr.demo.controller;import com.tr.demo.model.User;
import org.springframework.stereotype.Controller;@Controller
public class UserController6 {private User user;public UserController6(User user1) {this.user = user1;}public UserController6() {System.out.println("调用无参构造");}public void sayHi() {System.out.println("Hello, " + user);}
}

此时可以看到注入对象失败了,输出的结果是null

img
当然此时加上 @Autowired 注解就能正常注入了,就不做展示了。

3. Setter注入

Setter 注入就是在 setXXX 系列方法上加上 @Resource 或者 @Autowired 进行注入,和构造方法注入大同小异,简单演示一下。

package com.tr.demo.controller;import com.tr.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;@Controller
public class UserController7 {private User user;@Autowiredpublic void setUser(@Qualifier(value = "user2") User user) {this.user = user;}public void sayHi() {System.out.println("Hello, " + user);}
}

启动类和运行结果:

img

这里这里第一行输入的是因为启动程序会将上面写的UserController6也添加到容器中,UserController6的无参构造方法是我们自定义的。

三. 三种注入方式的优缺点

在早期的 Spring 版本中,官方推荐使用的 Setter 注入,最开始说的原因就是不符合单一设计原则吧,而现在比较新的 Spring 版本(Sring 4.x 之后)中,官方最使用推荐的又是构造方法注入了,说法是因为它的通用性最好。

🎯属性注入

优点:

  1. 使用起来简单方便

缺点:

  1. 无法注入到一个final修饰的变量,因为 final 修饰的变量只有两种赋值方式,一是直接赋值,二是通过构造方法进行赋值,而属性注入这两种方式都不能满足。img
  2. 通用性问题,属性注入只能在 IoC 容器中使用,在非 IoC 容器中是不可⽤的。
  3. 更容易违背单一设计原则,简单理解就是注入方式越简单,滥用的概率越大,就比如在数据持久层有一个针对用户操作的类,本来这个类就只是注入用户相关操作的依赖就行了,但由于属性注入使用起来成本不高,程序猿就多注了一些依赖去完成了一些和用户操作无关的内容,这就违背了单一设计原则了。

🎯Setter 注入

优点:

  1. 通常情况下,setXXX 系列的方法中只会设置一个属性,就更符合单一设计原则。

缺点:

  1. 同样的,也不能注入到一个 final 修饰的变量中。img
  2. 注入的对象是可能被修改的,因为 setXXX 系列的方法随时都有可能被调用导致注入的 Bean 就被修改了。

🎯构造方法注入

优点:

  1. 可以注入到一个被 final 修饰的变量。img
  2. 注入对象不会被修改,因为构造方法只会在对象创建时执行一次,不存在注入对象被随时修改的情况。
  3. 可以保证注入对象的完全初始化,因为构造方法是在对象创建之前执行的。
  4. 通用性最好,因为不管你怎么写 Java 代码,创建实例对象时都要执行构造方法吧。

缺点:

  1. 相较于属性注入,写法更加复杂,如果有多个注⼊会显得⽐较臃肿,但出现这种情况你应该考虑⼀下当前类是否符合程序的单⼀职责的设计模式了。
  2. 使用构造注入,无法解决循环依赖的问题。

四. 综合练习

在 Spring 项⽬中,通过 main ⽅法获取到 Controller 类,调⽤ Controller ⾥⾯通过注⼊的⽅式调⽤ Service 类,Service 再通过注⼊的⽅式获取到 Repository 类,Repository 类⾥⾯有⼀个⽅法构建⼀ 个 User 对象,返回给 main ⽅法。Repository ⽆需连接数据库,使⽤伪代码即可。

首先要清楚的是在 main 方法中是不能使用依赖注入的,因为类的静态部分是在 Spring 注入之前的加载的,仔细想一下,在类加载时就要使用一个还没注入的对象这是不现实的。

所以我们要在 main 中执行的是将扫描路径中的类添加到 Spring 中,对象的注入要在 mian 方法所在类的外部去实现。

img

package com.tr.demo.model;public class User {private int id;private String name;@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}package com.tr.demo.repository;import com.tr.demo.model.User;
import org.springframework.stereotype.Repository;@Repository
public class UserRepository {public User getUser(){// 伪代码User user = new User();user.setId(1);user.setName("张三");return user;}}package com.tr.demo.service;import com.tr.demo.model.User;
import com.tr.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService {@Autowiredprivate UserRepository userRepository;public User getUser(){return userRepository.getUser();}}package com.tr.demo.contoller;import com.tr.demo.model.User;
import com.tr.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller
public class UserController {@Autowiredprivate UserService userService;public User getUser(){return userService.getUser();}}package com.tr.demo;import com.tr.demo.contoller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** 启动类*/
public class App {public static void main(String[] args) {ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");UserController userController =context.getBean("userController", UserController.class);System.out.println(userController.getUser());}
}

运行结果:

img

相关文章:

Spring使用注解进行对象装配(DI)

文章目录 一. 什么是对象装配二. 三种注入方式1. 属性注入2. 构造方法注入3. Setter注入 三. 三种注入方式的优缺点四. 综合练习 通过五大类注解可以更便捷的将对象存储到 Spring 中,同样也可以使用注解将已经储存的对象取出来,直接赋值到注解所在类的一…...

数学建模-蒙特卡洛模拟

%% 蒙特卡罗用于模拟三门问题 clear;clc %% (1)预备知识 % randi([a,b],m,n)函数可在指定区间[a,b]内随机取出大小为m*n的整数矩阵 randi([1,5],5,8) %在区间[1,5]内随机取出大小为5*8的整数矩阵 % 2 5 4 5 3 1 4 2 %…...

Pearson correlation皮尔逊相关性分析

在参数检验的相关性分析方法主要是皮尔逊相关(Pearson correlation)。既然是参数检验方法,肯定是有一些前提条件。皮尔逊相关的前提是必须满足以下几个条件: 变量是连续变量;比较的两个变量必须来源于同一个总体&…...

P1036 [NOIP2002 普及组] 选数

题目描述 已知 &#xfffd;n 个整数 &#xfffd;1,&#xfffd;2,⋯ ,&#xfffd;&#xfffd;x1​,x2​,⋯,xn​&#xff0c;以及 11 个整数 &#xfffd;k&#xff08;&#xfffd;<&#xfffd;k<n&#xff09;。从 &#xfffd;n 个整数中任选 &#xfffd;k 个…...

css终极方案PostCSS

一见如故 原理 所有的css框架都在一样的事&#xff0c;那就是由一个css生成一个新的css&#xff0c;那么postcss就来做了一个抽离&#xff1a; 1、将原有的css解析成抽象语法树 2、中间经过若干个插件 3、重新文本化&#xff0c;形成新的css postcss.config.js module.expor…...

代码随想录算法训练营第三天|417. 太平洋大西洋水流问题|24. 两两交换链表中的节点|19.删除链表的倒数第N个节点|面试题 02.07. 链表相交|

417. 太平洋大西洋水流问题 水往高处流&#xff0c;先记录两个海祥往高处流所能留到的地址&#xff0c;之后将他们的合并区域进行输出 static const int dirs[4][2] {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};class Solution { public:vector<vector<int>> heights;v…...

【Java】Spring——创建Spring + 对Spring的存储 /读取对象操作

文章目录 前言一、创建Spring项目二、向Spring容器中存储 Bean 对象三、从Spring容器中读取 Bean 对象得到Spring上下文对象得到 Bean 对象 总结 前言 本人是一个普通程序猿!分享一点自己的见解,如果有错误的地方欢迎各位大佬莅临指导,如果你也对编程感兴趣的话&#xff0c;互…...

RTPSv2.2(中文版)

实时发布订阅协议 &#xff08;RTPS&#xff09; DDS互操作性 有线协议规范 V2.2 &#xff08;2014-09-01正式发布&#xff09; https://www.omg.org/spec/DDSI-RTPS/2.2/PDF 目 录 1 范围Scope 9 2 一致性Conformance 9 3 参考文献References 9 4 术语和定义Terms a…...

Django学习笔记-视图(views)的使用

Django中可以使用views进行管理&#xff0c;类似于WPF的MVVM的ViewModel层&#xff0c;也相当于MVC架构的模Controller层。 一、基于函数的视图FBV&#xff08;Function-Based View&#xff09; 通过定义一个函数&#xff0c;包含HttpRequest对象作为参数&#xff0c;用来接受…...

四姑娘山三日游

趁着小孩放暑假&#xff0c;从昆明回来之后&#xff0c;直接自驾到四姑娘山。 第一天 成都-四川省阿坝藏族羌族自治州小金县日隆镇(20230711) 大概9:30从成都市郫都区出发&#xff0c;路线如下&#xff1a;郫都—都江堰–映秀—耿达—卧龙—四姑娘山&#xff0c;中途翻过巴朗…...

spinal HDL语法学习

1 赋值语句 用来声明变量 : 用来对变量进行赋值 2 when otherwise前面是否有"."与otherwise是否换行有关系 3 case class 对Bundle进行扩展时&#xff0c;需要case class case class和class主要有两点区别&#xff1a; &#xff08;1&#xff09;case class不需…...

GRE TAP的工作原理与5G工业物联网中的应用

随着互联网新技术的发展以及智能化水平的提高&#xff0c;各企业对实时数据传输的需求也在不断提升&#xff0c;企业愈发重视数据中心的建设&#xff0c;以保障企业内网数据安全。 GRE&#xff08;Generic Routing Encapsulation&#xff0c;通用路由封装&#xff09;协议属于…...

NFT和数字藏品的安全方案解析

一、NFT和数字藏品 01 NFT是什么&#xff1f; NFT 是Non-Fungible Tokens 的缩写&#xff0c;意思是不可互换的代币&#xff0c;它是相对于可互换的代币而言的。不可互换的代币也称为非同质代币。什么是可互换的代币&#xff1f;比如BTC&#xff08;比特币&#xff09;、ETH&…...

第四篇-Miniconda3-CentOS7-安装

Miniconda3-CentOS7-安装 Conda可以创建你需要的不同版本的Python环境&#xff0c;做的各个环境之间隔离,可以有助于我们一台主机部署不同版本运行环境 下载 wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh执行 sh Miniconda3-latest-Linux-…...

高效率,38V最大输入单电感同步升/降稳压器SYV939C

SYV939是一种高压同步降压-升压转换器。该器件工作在4V至28V的宽输入电压范围内&#xff0c;具有10max平均电感电流能力。四个集成的低RDS(ON)开关最大限度地减少了传导损耗。 SYV939c包括完整的保护功能&#xff0c;如输出过流/短路保护&#xff0c;过压保护和热停机&#xff…...

mars3d绘制区域范围(面+边框)

1、图例&#xff08;绿色面区域白色边框&#xff09; 2、代码 1&#xff09;、绘制区域ts文件 import { mapLayerCollection } from /hooks/cesium-map-init /*** 安全防護目標* param map*/ export const addSafetyProtection async (map) > {const coverDatas await m…...

HTML的表格应用

HTML 中的表格用于在网页上展示和组织数据。表格由行和列组成&#xff0c;每个单元格可以包含文本、图像或其他 HTML 元素。下面是一些常用的 HTML 表格标签和属性的应用示例&#xff1a; <table> 标签: 定义表格的起始和结束标记。所有的表格元素应该放在这对标签之间。…...

android的数据存储方式

android的数据存储方式 Android提供了多种数据存储方式&#xff0c;开发者可以根据具体的需求选择合适的存储方式。以下是Android中常用的数据存储方式&#xff1a; Shared Preferences&#xff08;共享偏好设置&#xff09;&#xff1a; Shared Preferences允许将简单的键值…...

用C++编写一个MyString类

1.平台&#xff1a;vs2019 2.很多知识点写在了代码里 class MyString { private:char* str;MyString(char* p, int x){str p;} public:MyString(const char* p nullptr) :str(nullptr){if (p ! nullptr){int len strlen(p) 1;str new char[len];strcpy_s(str, len, p);}…...

Linux C语言中access函数的用法

access()函数的用法&#xff1a;int access(const char *filenpath, int mode) 一、access()函数的作用 access()函数用来判断某个指定路径的文件&#xff08;第一个参数 filenpath&#xff09;&#xff0c;是否符合第二个参数选项&#xff08;F_OK(文件是否存在&#xff09;…...

c# winform 子窗体关闭时主窗体执行指令

按下一个按钮打开子窗体&#xff0c;点 x 关闭子窗体后主窗体自动执行某些指令。例如刷新窗体&#xff0c;加载数据等。 点 x 关闭子窗体后将会执行"刷新父窗体2"下面的内容&#xff0c;其他的没试。 Config readConfigTest new Config();//new一个子窗体并打开 re…...

vue-simple-uploader的fileAdded方法不支持异步的解决办法,autoStart 设置

每日鸡汤&#xff1a;悲观者可能正确&#xff0c;但是乐观者往往成功 假设有一个需求&#xff0c;上传的pdf文档不得大于10M 使用 vue-simple-uploader 这个插件&#xff0c;我们需要在 fileAdded 事件里面进行校验&#xff0c;在1.0.0版本以后&#xff0c;如果想停止上传&…...

WormGPT – 网络犯罪分子用来犯罪的人工智能工具

WormGPT – 网络犯罪分子用来发起商业电子邮件泄露攻击的生成式人工智能工具 前言 什么是蠕虫GPT&#xff08;WormGPT&#xff09; WormGPT是基于EleutherAI于2021年创建的大型语言模型GPT-J的AI模型。它具有无限的字符支持、聊天记忆保留和代码格式化功能。 如果未部署适当…...

【NLP】语音识别 — GMM, HMM

一、说明 在语音识别的深度学习&#xff08;DL&#xff09;时代之前&#xff0c;HMM和GMM是语音识别的两项必学技术。现在&#xff0c;有将HMM与深度学习相结合的混合系统&#xff0c;并且有些系统是免费的HMM。我们现在有更多的设计选择。然而&#xff0c;对于许多生成模型来说…...

中间件面试题

Redis相关面试题 此题是xx位面试题 (1)Redis Cluster,在一个请求过来的时候,数据分布在哪个节点, 使用的是有哪些算法? redis cluster 用不同的算法,就决定了数据如何分布到这些节点上去。 hash算法一致性hash算法hash slot算法(2)Redis为什么是高性能的? 基于内存…...

PHP使用Redis实战实录2:Redis扩展方法和PHP连接Redis的多种方案

PHP使用Redis实战实录系列 PHP使用Redis实战实录1&#xff1a;宝塔环境搭建、6379端口配置、Redis服务启动失败解决方案PHP使用Redis实战实录2&#xff1a;Redis扩展方法和PHP连接Redis的多种方案 Redis扩展方法和PHP连接Redis的多种方案 一、Redis扩展方法二、php操作Redis语…...

【Docker】Docker应用部署之Docker容器安装Redis

目录 一、搜索Redis镜像 二、拉取Redis镜像 三、创建容器 四、测试使用 一、搜索Redis镜像 docker search redis 二、拉取Redis镜像 docker pull redis:版本号 # 拉取对应版本的redis镜像 eg: docker pull redis:5.0 三、创建容器 docker run -id --nameredis -p 6379:637…...

【C++】STL——list的介绍和使用、list增删查改函数的介绍和使用、push_back、pop_back

文章目录 1.list的使用2.list的增删查改函数&#xff08;1&#xff09;push_front 在list首元素前插入值为val的元素&#xff08;2&#xff09;pop_front 删除list中第一个元素&#xff08;3&#xff09;push_back 在list尾部插入值为val的元素&#xff08;4&#xff09;pop_ba…...

“RWEQ+”集成技术在土壤风蚀模拟与风蚀模数估算、变化归因分析中的实践

土壤风蚀是一个全球性的环境问题。中国是世界上受土壤风蚀危害最严重的国家之一&#xff0c;土壤风蚀是中国干旱、半干旱及部分湿润地区土地荒漠化的首要过程。中国风蚀荒漠化面积达160.74104km2&#xff0c;占国土总面积的16.7%&#xff0c;严重影响这些地区的资源开发和社会经…...

ChatGPT在智能推送和个性化广告中的应用如何?

ChatGPT在智能推送和个性化广告领域具有广泛的应用潜力。智能推送和个性化广告是指根据用户的个性化需求和兴趣&#xff0c;精准地向用户推送相关的信息和广告内容。ChatGPT作为一种预训练的通用语言模型&#xff0c;具有强大的语言理解和生成能力&#xff0c;可以在智能推送和…...