2023.11.7 Spring 依赖注入的三大方式

目录

前言

属性注入(@Autowired)

Setter 注入

构造方法注入

 @Resource

@Autowired 和 @Resource 的区别

@Autowired 和 @Resource 查找 Bean 对象的区别


前言

配置文件

​
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:content="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!--base-package 表示要扫描的路径--><content:component-scan base-package="com.java.demo"></content:component-scan>
</beans>​

启动类

import com.java.demo.controller.StudentController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {public static void main(String[] args) {
//        得到 Spring 上下文对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//        获取 Bean 对象StudentController studentController  = context.getBean("studentController",StudentController.class);studentController.sayHi();}
}
  • 在启动类中,不能使用下述三种注入方式来实现对象的注入
  • 运行启动类时,需要执行 main 方法,然而 main 方法被 static 关键字所修饰的,即 main 方法为静态方法
  • 静态方法的加载顺序是高于 Spring 容器初始化的
  • 所以在该静态方法中,我们无法使用以下三种依赖注入的方式来获取 Bean 对象
  • 所以在下文 举例实现这三种依赖注入的方式 时,我们还是会通过 启动类 来获取 StudentController 的 Bean 对象
  • 但是会在 StudentController 类中使用以下三种依赖注入的方式,来实现在StudentController 类中成功注入 UserService 类的 Bean 对象

属性注入(@Autowired)

  • 日常开发中,属性注入是我们最常用的一种注入方式

实例

import com.java.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;// 使用 @Controller 注解将当前类存储到 spring 容器中
@Controller
public class StudentController {@Autowired // 注入 UserService 的 Bean 对象private UserService userService;public void sayHi() {System.out.println("student controller say hi");
//        直接调用执行 userService 的成员方法userService.sayHi();}
}

运行启动类的结果:


重点理解

使用属性注入时:

  • 首先 ApplicationContext 在 Spring 容器启动时就会被实例化和初始化,并它会在启动过程中创建和管理所有的 Bean 对象
  • 随后容器在创建 StudentController 类的 Bean 对象时,会自动检测 StudentController 类的依赖关系并注入相应的依赖对象
  •  UserService 的 Bean 对象,便在此时由 Spring 容器自动注入给了 StudentController 类
  • 即 StudentController 类中的 userService 对象被 Spring 容器直接赋值

优点

  • 实现简单、使用简单
  • 只需给变量上添加一个注解(@Autowired),即可获得注入的 Bean 对象

缺点

1、无法实现 final 修饰的变量注入

  • 在 Java 中被 final 关键字修饰的变量被称为常量
  • 常量的声明和初始化需要在同一时间完成,且只能被赋值一次
  • 方式一:直接赋值
  • 方式二:通过构造方法赋值

2、只适用于 IoC 容器(兼容问题)

  • 如果将属性注入的代码移植到其他非 IoC 的框架中,该代码就无效了,所以其兼容性有限

3、因为写法简单,所以违背单一职责原则的概率更大(存在风险)

  • 单一职责原则 是面向对象设计中的一个重要原则,它指出一个类应该有且只有一个引起它变化的原因
  • 此处强调的是违背单一职责原则的可能性,而不是一定会违背单一职责原则,这与程序员自己的代码强相关

Setter 注入

实例

import com.java.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;// 使用 @Controller 注解将当前类存储到 spring 容器中
@Controller
public class StudentController {//    使用 Setter 注入private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}public void sayHi() {System.out.println("student controller say hi");
//        直接调用执行 userService 的成员方法userService.sayHi();}
}
  • 此处需要使用 @Autowired 添加到 Setter 方法上
  • 代表着 Spring 会针对该 Setter 方法 的参数进行相应的赋值

运行启动类执行结果:


重点理解

使用 Setter 注入时:

  • 首先 ApplicationContext 在 Spring 容器启动时就会被实例化和初始化,并它会在启动过程中创建和管理所有的 Bean 对象
  • 随后容器在创建 StudentController 类的 Bean 对象时,会自动检测 StudentController 类的依赖关系并注入相应的依赖对象
  •  UserService 的 Bean 对象,便在此时由 Spring 容器自动注入给了 StudentController 类
  • 即 StudentController 类中的 setUserService 方法的参数直接被 Spring 容器给赋值
  • 然后通过方法中的 this.userService = userService 语句
  • 将被 Spring 容器赋值的 userService 对象,传递赋值给 StudentController 类中userService 变量
  • 此刻 StudentController 类便可以成功使用 UserService 类的 Bean 对象了

缺点

1、同样无法实现 final 修饰的变量注入

2、注入对象可被修改

import com.java.demo.controller.StudentController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {public static void main(String[] args) {
//        得到 Spring 上下文对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//        获取 Bean 对象StudentController studentController  = context.getBean("studentController",StudentController.class);
//        setUserService 可以被随便调用,然后改变原先已经被 Spring 容器注入好的 userService Bean 对象
//        此处直接将 studentController 对象中的 userService 变量的值改为 nullstudentController.setUserService(null);
//        执行到此处时便会发生报错studentController.sayHi();}
}

运行结果:

图解原因:

构造方法注入

  • 构造方法注入是 Spring 官方推荐的注入方式
  • 但是日常开发用的最多的还是属性注入

特点:

  • 如果当前类中只有一个构造方法的话, @Autowired 注解可以省略

实例

import com.java.demo.service.UserService;
import org.springframework.stereotype.Controller;// 使用 @Controller 注解将当前类存储到 spring 容器中
@Controller
public class StudentController {//    使用 构造方法 注入不可变对象private final UserService userService;//    @Autowired  因为当前类仅一个构造方法,所以此处的 @Autowired 可以省略public  StudentController (UserService userService) {this.userService = userService;}public void sayHi() {System.out.println("student controller say hi");userService.sayHi();}
}

运行结果:


优点

1、可以注入一个不可变对象(即用 final 关键字修饰的对象)

  • 被 final 关键字修饰的对象必须满足以下两个条件中的任意一个
  • final 修饰的对象,要么需直接赋值
  • final 修饰的对象,要么需通过构造方法赋值

2、注入对象后不会被修改

  • 构造方法随着类加载仅执行一次

3、构造方法注入可以保证对象完全被完全初始化

  • 当创建一个对象时,一定会调用该类的构造方法来初始化该对象
  • 所以通过构造方法注入时,将必然保证该对象已经是被 Spring 容器所注入了 Bean 对象的

4、相比属性注入,构造方法注入的兼容性更好

  • 构造方法注入可适用于任何环境,无论是 IoC 框架还是非 IoC 框架

 @Resource

  • @Resource 注解由 JDK 所提供

@Autowired 和 @Resource 的区别

  • 出身不同:@Autowired 来自于 Spring,而 @Resource 来自于 JDK
  • @Autowired 可用于 属性注入、Settter 注入、构造函数注入,而 @Resource 只能用于 属性注入、Setter 注入
  • 使用时设置的参数不同:相比于 @Autowired 来说,@Resource 支持更多的参数设置,例如 重命名

@Autowired 和 @Resource 查找 Bean 对象的区别

  • @Autowired 先会根据类型查找,之后再根据名称查找
  • 而 @Resource 会先根据名称来查,再根据类型查找

实例理解

  • 我们创建一个实体类
// 普通的用户实体类
public class User {public Integer uid;public String username;public String password;public Integer age;public Integer getUid() {return uid;}public void setUid(Integer uid) {this.uid = uid;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}
}
  • 再创建一个 UserBean 类,通过该类向 Spring 容器中注入 User 的 Bean 对象
import com.java.demo.entity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;@Component
public class UserBeans {@Beanpublic User getUserByName() {User user = new User();user.setUid(1);user.setUsername("张三");user.setPassword("123456");user.setAge(18);return user;}@Bean(name = {"user1","u1"})public User getUserById() {User user = new User();user.setUid(1);user.setUsername("李四");user.setPassword("123456");user.setAge(18);return user;}
}
  •  User 的 Bean 对象通过 @Autowired 注入到 StudentController 类中
import com.java.demo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;// 使用 @Controller 注解将当前类存储到 spring 容器中
@Controller
public class StudentController {//    使用 属性 注入@Autowiredprivate User user;public void sayHi() {System.out.println("student controller say hi");user.getUsername();}
}

运行结果:

报错分析:

  • 正因为我们在 UserBeans 类中向 Spring 容器注入了两个同为 User 类型的 Bean 对象
  • 所以当我们使用 @Autowired 注解来注入 Bean 对象的时候,它会先根据类型来查找

  • 那此时便在 Spring 容器中查找到了 两个同为 User 类型的 Bean 对象 
  • 从而 将会根据绿框名称来查找,若 Spring 容器中有 id 为 user 的 Bean 对象,便会直接注入

  • 但是我们存入 Spring 容器中的 User 类型的 Bean 对象,其一 id 为 getUserByName,其二 id 为 user1 或 u1
  • 所以 @Autowired 找不到 id 为 user 的 Bean 对象,从而发生报错

解决方法:

方案一:

  • 修改绿框名称,通过 id 来指定注入一个 Bean 对象 

方案二:

  • 使用 @Resource 注解,通过设置参数来查找

  • 当然本应将参数名称设置为 user1 来实现依赖注入的
  • 使用该方式,可以实现 参数名称 的重命名

方案三:

  • 组合使用 @Autowired 和 @Qualifier 注解

  • @Qualifier 注解起到筛选的作用,筛选出 id 为 user1 的 Bean 对象,并将其注入
  • 该方式所呈现的效果 与 方式二 相同

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uniapp在不需要后端数据的情况下 怎么记录用户进一次记录一次

目录 前言&#xff1a; html部分 js部分 完整代码 前言&#xff1a; 一时兴起&#xff0c;不喜勿喷&#xff0c;今天听到了这个问题想到了一个方法&#xff0c;解决方式如下。 html部分 他用于显示访问次数&#xff08;visitCount变量的值&#xff09;。 <template&…

MySQL主从搭建,实现读写分离(基于docker)

一 主从配置原理 mysql主从配置的流程大体如图&#xff1a; 1&#xff09;master会将变动记录到二进制日志里面&#xff1b; 2&#xff09;master有一个I/O线程将二进制日志发送到slave; 3) slave有一个I/O线程把master发送的二进制写入到relay日志里面&#xff1b; 4&#xf…

万字长文 - Python 日志记录器logging 百科全书 之 基础配置

万字长文 - Python 日志记录器logging 百科全书 之 基础配置 前言 在日常的开发中工作中&#xff0c;日志记录扮演着不可或缺的角色。它不仅能让我们了解应用程序的运行状况&#xff0c;还能帮助我们定位并解决各种问题。 最基本的&#xff0c;它记录了应用程序的运行情况&am…

SBCFormer:快速、轻量级神经网络 for 边缘AI

一&#xff1a; SBCFormer [ PDF] SBCFormer 是一种 CNN-ViT 轻量混合网络backbone&#xff0c;能在单板计算机上能以每秒 1 帧速度进行全尺寸 ImageNet 分类的轻量级网络。 通过在低端 CPU 上实现高准确性和快速计算&#xff0c;为树莓派 4 型号 B 的 ARM-Cortex A72 CPU 提供…

情感分析与LLMs角色扮演

深度学习自然语言处理 原创作者&#xff1a;wkk 就像人类在做一件事情的时候&#xff0c;可能需要尝试多次。LLM也是如此&#xff01;这对于情感分析任务尤其如此&#xff0c;在情感分析任务中&#xff0c;LLM需要深入推理来处理输入中的复杂语言现象&#xff08;例如&#xff…

「Qt Widget中文示例指南」如何模拟一个时钟?

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 点击获取Qt Widget组…

康耐视深度学习ViDi-ViDi四大工具之一蓝色定位工具/Locate

目录 工具介绍使用步骤说明调整工具ROI添加特征标签生成定位姿态训练并审核模型编辑器参数说明蓝色定位工具/Locate工具 工具介绍 蓝色定位工具用于识别和定位图像中的特定特征或特征组。该工具的输出可用于为其他ViDi 工具提供位置数据。使用该工具时,您提供图像训练集,然后…

Apache Doris (五十二): Doris Join类型 - Broadcast Join

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录 1. Broadcast Join原理

Vue+OpenLayers 创建地图并显示鼠标所在经纬度

1、效果 2、创建地图 本文用的是高德地图 页面 <div class"map" id"map"></div><div id"mouse-position" class"position_coordinate"></div>初始化地图 var gaodeLayer new TileLayer({title: "高德地…

python数据分析及可视化(十五)数据分析可视化实战篇(抖音用户数据分析、二手房数据分析)

python数据分析的实战篇&#xff0c;围绕实例的数据展开分析&#xff0c;通过数据操作案例来了解数据分析中的频繁用到的知识内容。 抖音用户数据分析 1.理解数据 数据字段含义 了解数据内容&#xff0c;确保数据来源是正常的&#xff0c;安全合法的。理解一下每一个字段的…

C站你好,和你相遇的第1825天

文章目录 机缘收获日常成就憧憬 机缘 ①. 你好,C站 ②. 初识JAVA编程,遇到问题,粘贴问题百度搜索,大都数出来的解决方案都能在C站得到解决,对C站有一定的好感 ③. 起初在CSDN写博客,主要用来记录日常学习过程中的笔记、不断调整自己的笔记,如JAVA基础、框架、虚拟机等,为后…

java传base64返回给数据报404踩坑

一、问题复现 1.可能因为base64字符太长&#xff0c;导致后端处理时出错&#xff0c;表现为前端请求报400错误&#xff1b; 这一步debug进去发现base64数据是正常传值的 所以排除掉不是后端问题,但是看了下前端请求,猜测可能是转换base64时间太长数据过大导致的404 2.前端传…

聚观早报 |GPT-4周活用户数达1亿;长城汽车10月销量增加

【聚观365】11月8日消息 GPT-4周活用户数达1亿 长城汽车10月销量增加 xAI宣布推出PromptIDE工具 aigo爱国者连发5款儿童手表 SpaceX预计今年营收90亿美元 GPT-4周活用户数达1亿 在OpenAI首届开发者大会上&#xff0c;该公司首席执行官萨姆奥特曼&#xff08;Sam Altman&a…

删除word最后一页之后的空白页

最近编辑word比较多&#xff0c;有时最后一页&#xff08;最后一页内容还有可能是表格&#xff09;之后&#xff0c;还有一页空白页&#xff0c;单独按下backspace、del都删不掉&#xff0c;很让人着急。 经过查询有几种方法&#xff1a; &#xff08;1&#xff09;点击选中空…

C#中基于.NET6的动态编译技术

前几天要解决动态计算问题&#xff0c;尝试着使用了不同的方法。问题是给定一个包含计算的字符串&#xff0c;在程序运行中得到计算结果&#xff0c;当时考虑了动态编译&#xff0c;在网上查了一些资料完成了这项功能&#xff0c;可是基于不同的.NET平台使用的编程代码相差比较…

Spring Data JPA 项目配置与QueryDSL集成

一、说明 Spring Data JPA通过Spring Initializer创建时勾选相关依赖即可引入&#xff0c;QueryDSL需要单独引入。Spring JPA针对QueryDSL有比较好的兼容性&#xff0c;可以实现优雅的SQL构建。 二、设置JPA默认配置&#xff08;yaml格式&#xff09; spring:jpa:hibernate:…

【Linux】:使用git命令行 || 在github创建项目 || Linux第一个小程序——进度条(进阶版本)

在本章开始之前还是先给大家分享一张图片 这是C的笔试题 感兴趣的同学可以去试一试 有难度的哟 也可以直接在牛客网直接搜索这几道题目哈 好了今天我们正式进入我们的正题部分 &#x1f556;1.使用git命令行 安装git yum install git&#x1f560;2.在github创建项目 使用…

各省市90米分辨率DEM数据,多图可下载

之前给大家推了30米分辨率dem数据&#xff0c;有些小伙伴反应也需要90米的&#xff0c;于是今天就给大家推荐一个新数据 —— 各省市90米分辨率DEM数据&#xff01; 各省市90米分辨率DEM数据广泛应用于国土资源调查、水利水电工程、地质灾害预警、城市规划等领域&#xff0c;对…

10道高频Vuex面试题快问快答

※其他的快问快答&#xff0c;看这里&#xff01; 10道高频Qiankun微前端面试题快问快答 10道高频webpack面试题快问快答 20道高频CSS面试题快问快答 20道高频JavaScript面试题快问快答 30道高频Vue面试题快问快答 面试中的快问快答 快问快答的情景在面试中非常常见。 在面试过…

公开IP属地信息如何保护用户的隐私?

公开IP属地信息通常涉及与用户或组织的隐私有关&#xff0c;因此在公开此类信息时需要非常小心&#xff0c;以避免侵犯他人的隐私权。以下是触碰底线的几种情况以及如何保护网络安全和用户隐私&#xff1a; 个人隐私保护&#xff1a; 公开IP属地信息可能泄露用户的物理位置&…