Spring使用注解进行对象装配(DI)
文章目录
- 一. 什么是对象装配
- 二. 三种注入方式
- 1. 属性注入
- 2. 构造方法注入
- 3. Setter注入
- 三. 三种注入方式的优缺点
- 四. 综合练习
通过五大类注解可以更便捷的将对象存储到 Spring 中,同样也可以使用注解将已经储存的对象取出来,直接赋值到注解所在类的一个属性中,这一个过程也叫做对象的装配或者叫对象的注入,即 DI。
一. 什么是对象装配
获取 Bean 对象也叫做对象装配,就是把对象取出来放到某个类中,有时候也叫对象注入。
对象装配(对象注入)的实现方法以下 3 种:
- 属性注入 ,就是将对象注入到某个类的一个属性当中。
- 构造方法注入 ,就是通过构造方法来将对象注入到类中。
- Setter 注入 ,通过 SetXXX 系列方法将对象注入到类中。
常见有关对象注入的注解有两个,一个是@Autowired,另外一个是@Resource
🍂它们两者的区别如下:
- 出身不同:@Autowired 是由
Spring提供的,而 @Resource 是JDK提供的。 - 查找顺序不同:从容器中获取对象时 @Autowired 先根据类型再根据名称查询,而 @Resource 先根据名称再根据类型查询。
- 使⽤时设置的参数不同:@Resource 支持更多的参数设置(有 7 个),如
name、type等,而@Autowired只支持设置required一个参数,用来设置注入 Bean 的时候该 Bean 是否必须存在(true/false)。

- 依赖注入支持不同:@Autowired 支持属性注入,构造方法注入和 Setter 注入,而 @Resource 只支持属性注入和 Settter 注入,但是不支持构造方法注入。
- 对 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();}
}
运行结果:

上面说的是同类对象只有一个的情况,而如果存在多个同类对象,我们就得通过参数来告知容器我们要注入哪一个对象,不告知就会报错。
比如我们将多个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方法:

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

此时就需要我们来告知容器我们需要哪一个具体的 Bean,要获得目标对象主要有下面三种方法:
- 1️⃣方法1:将属性的变量名设置为你需要的那个
BeanName就可以了,后面的构造方法与 Setter 注入同理,将形参名设置成与BeanName相同即可。

- 2️⃣方法2:@Autowired 注解与 @Qualifier 注解配合使用,设置 @Qualifier 的
value参数为BeanName即可,要注意 @Qualifier 注解不能修饰方法,只能修饰变量。


- 3️⃣方法3:将 @Autowired 注解替换成 @Resource 注解的,并将它
name参数值设置为BeanName即可。

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);}
}
启动类就不贴代码了,一样的,运行结果如下:

方法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);}
}
运行结果:

对了,如果一个类中只有一个构造方法,@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);}
}
此时仍然是可以成功注入对象。

如果有多个构造方法,要注意此时是不能省略 @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。

当然此时加上 @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);}
}
启动类和运行结果:

这里这里第一行输入的是因为启动程序会将上面写的UserController6也添加到容器中,UserController6的无参构造方法是我们自定义的。
三. 三种注入方式的优缺点
在早期的 Spring 版本中,官方推荐使用的 Setter 注入,最开始说的原因就是不符合单一设计原则吧,而现在比较新的 Spring 版本(Sring 4.x 之后)中,官方最使用推荐的又是构造方法注入了,说法是因为它的通用性最好。
🎯属性注入
优点:
- 使用起来简单方便
缺点:
- 无法注入到一个
final修饰的变量,因为 final 修饰的变量只有两种赋值方式,一是直接赋值,二是通过构造方法进行赋值,而属性注入这两种方式都不能满足。
- 通用性问题,属性注入只能在 IoC 容器中使用,在非 IoC 容器中是不可⽤的。
- 更容易违背单一设计原则,简单理解就是注入方式越简单,滥用的概率越大,就比如在数据持久层有一个针对用户操作的类,本来这个类就只是注入用户相关操作的依赖就行了,但由于属性注入使用起来成本不高,程序猿就多注了一些依赖去完成了一些和用户操作无关的内容,这就违背了单一设计原则了。
🎯Setter 注入
优点:
- 通常情况下,setXXX 系列的方法中只会设置一个属性,就更符合单一设计原则。
缺点:
- 同样的,也不能注入到一个 final 修饰的变量中。

- 注入的对象是可能被修改的,因为 setXXX 系列的方法随时都有可能被调用导致注入的 Bean 就被修改了。
🎯构造方法注入
优点:
- 可以注入到一个被 final 修饰的变量。

- 注入对象不会被修改,因为构造方法只会在对象创建时执行一次,不存在注入对象被随时修改的情况。
- 可以保证注入对象的完全初始化,因为构造方法是在对象创建之前执行的。
- 通用性最好,因为不管你怎么写 Java 代码,创建实例对象时都要执行构造方法吧。
缺点:
- 相较于属性注入,写法更加复杂,如果有多个注⼊会显得⽐较臃肿,但出现这种情况你应该考虑⼀下当前类是否符合程序的单⼀职责的设计模式了。
- 使用构造注入,无法解决循环依赖的问题。
四. 综合练习
在 Spring 项⽬中,通过 main ⽅法获取到 Controller 类,调⽤ Controller ⾥⾯通过注⼊的⽅式调⽤ Service 类,Service 再通过注⼊的⽅式获取到 Repository 类,Repository 类⾥⾯有⼀个⽅法构建⼀ 个 User 对象,返回给 main ⽅法。Repository ⽆需连接数据库,使⽤伪代码即可。
首先要清楚的是在 main 方法中是不能使用依赖注入的,因为类的静态部分是在 Spring 注入之前的加载的,仔细想一下,在类加载时就要使用一个还没注入的对象这是不现实的。
所以我们要在 main 中执行的是将扫描路径中的类添加到 Spring 中,对象的注入要在 mian 方法所在类的外部去实现。

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());}
}
运行结果:

相关文章:
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 普及组] 选数
题目描述 已知 �n 个整数 �1,�2,⋯ ,��x1,x2,⋯,xn,以及 11 个整数 �k(�<�k<n)。从 �n 个整数中任选 �k 个…...
css终极方案PostCSS
一见如故 原理 所有的css框架都在一样的事,那就是由一个css生成一个新的css,那么postcss就来做了一个抽离: 1、将原有的css解析成抽象语法树 2、中间经过若干个插件 3、重新文本化,形成新的css postcss.config.js module.expor…...
代码随想录算法训练营第三天|417. 太平洋大西洋水流问题|24. 两两交换链表中的节点|19.删除链表的倒数第N个节点|面试题 02.07. 链表相交|
417. 太平洋大西洋水流问题 水往高处流,先记录两个海祥往高处流所能留到的地址,之后将他们的合并区域进行输出 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 对象 总结 前言 本人是一个普通程序猿!分享一点自己的见解,如果有错误的地方欢迎各位大佬莅临指导,如果你也对编程感兴趣的话,互…...
RTPSv2.2(中文版)
实时发布订阅协议 (RTPS) DDS互操作性 有线协议规范 V2.2 (2014-09-01正式发布) 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进行管理,类似于WPF的MVVM的ViewModel层,也相当于MVC架构的模Controller层。 一、基于函数的视图FBV(Function-Based View) 通过定义一个函数,包含HttpRequest对象作为参数,用来接受…...
四姑娘山三日游
趁着小孩放暑假,从昆明回来之后,直接自驾到四姑娘山。 第一天 成都-四川省阿坝藏族羌族自治州小金县日隆镇(20230711) 大概9:30从成都市郫都区出发,路线如下:郫都—都江堰–映秀—耿达—卧龙—四姑娘山,中途翻过巴朗…...
spinal HDL语法学习
1 赋值语句 用来声明变量 : 用来对变量进行赋值 2 when otherwise前面是否有"."与otherwise是否换行有关系 3 case class 对Bundle进行扩展时,需要case class case class和class主要有两点区别: (1)case class不需…...
GRE TAP的工作原理与5G工业物联网中的应用
随着互联网新技术的发展以及智能化水平的提高,各企业对实时数据传输的需求也在不断提升,企业愈发重视数据中心的建设,以保障企业内网数据安全。 GRE(Generic Routing Encapsulation,通用路由封装)协议属于…...
NFT和数字藏品的安全方案解析
一、NFT和数字藏品 01 NFT是什么? NFT 是Non-Fungible Tokens 的缩写,意思是不可互换的代币,它是相对于可互换的代币而言的。不可互换的代币也称为非同质代币。什么是可互换的代币?比如BTC(比特币)、ETH&…...
第四篇-Miniconda3-CentOS7-安装
Miniconda3-CentOS7-安装 Conda可以创建你需要的不同版本的Python环境,做的各个环境之间隔离,可以有助于我们一台主机部署不同版本运行环境 下载 wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh执行 sh Miniconda3-latest-Linux-…...
高效率,38V最大输入单电感同步升/降稳压器SYV939C
SYV939是一种高压同步降压-升压转换器。该器件工作在4V至28V的宽输入电压范围内,具有10max平均电感电流能力。四个集成的低RDS(ON)开关最大限度地减少了传导损耗。 SYV939c包括完整的保护功能,如输出过流/短路保护,过压保护和热停机ÿ…...
mars3d绘制区域范围(面+边框)
1、图例(绿色面区域白色边框) 2、代码 1)、绘制区域ts文件 import { mapLayerCollection } from /hooks/cesium-map-init /*** 安全防護目標* param map*/ export const addSafetyProtection async (map) > {const coverDatas await m…...
HTML的表格应用
HTML 中的表格用于在网页上展示和组织数据。表格由行和列组成,每个单元格可以包含文本、图像或其他 HTML 元素。下面是一些常用的 HTML 表格标签和属性的应用示例: <table> 标签: 定义表格的起始和结束标记。所有的表格元素应该放在这对标签之间。…...
android的数据存储方式
android的数据存储方式 Android提供了多种数据存储方式,开发者可以根据具体的需求选择合适的存储方式。以下是Android中常用的数据存储方式: Shared Preferences(共享偏好设置): Shared Preferences允许将简单的键值…...
用C++编写一个MyString类
1.平台: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()函数的用法:int access(const char *filenpath, int mode) 一、access()函数的作用 access()函数用来判断某个指定路径的文件(第一个参数 filenpath),是否符合第二个参数选项(F_OK(文件是否存在)…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
