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();
    }
}

运行启动类的结果:


重点理解

使用属性注入时:

  • 在 Spring 容器启动时,ApplicationContext 实例将被实例化和初始化,它负责加载配置文件(如 XML 文件)并创建和管理所有的 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;

    @Autowired
    public 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 注入时:

  • 在 Spring 容器启动时,ApplicationContext 实例将被实例化和初始化,它负责加载配置文件(如 XML 文件)并创建和管理所有的 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 变量的值改为 null
        studentController.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 {
    @Bean
    public 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 {

    //    使用 属性 注入
    @Autowired
    private 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 对象,并将其注入
  • 该方式所呈现的效果 与 方式二 相同

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

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

相关文章

kafka微服务学习

消息中间件对比&#xff1a; 1、吞吐、可靠性、性能 Kafka安装 Kafka对于zookeeper是强依赖&#xff0c;保存kafka相关的节点数据&#xff0c;所以安装Kafka之前必须先安装zookeeper Docker安装zookeeper 下载镜像&#xff1a; docker pull zookeeper:3.4.14创建容器 do…

【Redis缓存架构实战常见问题剖析】

文章目录 一、Redis缓存架构实战剖析1.1、大规模的商品缓存数据冷热分离机制1.2、缓存击穿导致线上数据压力暴增解决方案1.3、缓存穿透及其解决方案剖析1.4、突发性的热点缓存数重建导致系统压力暴增问题分析1.5、Redis分布式锁解决缓存与数据库双写不一致问题剖析1.6、利用多级…

Python机器学习算法入门教程(第四部分)

接着Python机器学习算法入门教程&#xff08;第三部分&#xff09;&#xff0c;继续展开描述。 十九、信息熵是什么 通过前两节的学习&#xff0c;我们对于决策树算法有了大体的认识&#xff0c;本节我们将从数学角度解析如何选择合适的“特征做为判别条件”&#xff0c;这里…

微服务 Spring Cloud 5,一图说透Spring Cloud微服务架构

目录 一、域名系统DNS二、LVS&#xff08;Linux Virtual Server&#xff09;,Linux虚拟服务器三、CDN静态资源四、Nginx反向代理服务器1、Nginx的主要作用体现在以下几个方面&#xff1a;2、Nginx静态资源服务和CDN静态资源服务&#xff0c;如何选择&#xff1f; 五、Gateway网…

C#上位机序列10: Winform上位机通用框架

C#上位机序列1: 多线程&#xff08;线程同步&#xff0c;事件触发&#xff0c;信号量&#xff0c;互斥锁&#xff0c;共享内存&#xff0c;消息队列&#xff09; C#上位机序列2: 同步异步(async、await) C#上位机序列3: 流程控制&#xff08;串行&#xff0c;并行&#xff0c…

Panorama SCADA平台的警报通知功能配置详解

1. 前言 SCADA系统的主要目标是采集与监控工业过程数据&#xff0c;以确保工业生产正常运行。通过实时警报通知功能&#xff0c;操作人员可以立即获取有关潜在问题的信息&#xff0c;从而能够快速采取行动解决问题&#xff0c;防止进一步的损害或生产中断。因此&#xff0c;及…

三相电机的某些实测特性曲线

三相电机参数&#xff1a; 0.75KW&#xff0c;额定电流是2A&#xff0c;功率因数0.71&#xff0c;效率78.9%。制式S1. 1.负载不变时的线电压与线电流的关系 1.1相关数据与python代码&#xff1a; 这里记录了一系列的实验&#xff1a; 第一组实验&#xff1a;近乎空载&#xf…

企业微信开启接收消息+验证URL有效性

企业微信开启接收消息验证URL有效性 &#x1f4d4; 千寻简笔记介绍 千寻简笔记已开源&#xff0c;Gitee与GitHub搜索chihiro-notes&#xff0c;包含笔记源文件.md&#xff0c;以及PDF版本方便阅读&#xff0c;且是用了精美主题&#xff0c;阅读体验更佳&#xff0c;如果文章对…

[Framework] Android Handler 工作原理

作者&#xff1a;Tans5 Android 中的 Handler 都被人说烂了&#xff0c;但是还是想多说一次&#xff0c;因为在 Android 的系统中它真的非常重要而且它的机制并没有很复杂&#xff0c;无论是新手和老手都可以好好学习下&#xff0c;这对理解 Android 系统很重要&#xff0c;所以…

如何进行网站测试

随着市场和技术的快速发展&#xff0c;产品需要不断更新和改进以保持竞争力&#xff0c;如果产品停滞不前&#xff0c;很可能会被市场淘汰。通过持续发展&#xff0c;企业可以不断优化产品&#xff0c;提高用户体验&#xff0c;从而赢得市场份额和客户忠诚度。而数通在激烈的市…

计算机毕业设计项目选题推荐(免费领源码)Java+springboot+Mysql停车微信小程序小程序92714

摘 要 在信息飞速发展的今天&#xff0c;网络已成为人们重要的信息交流平台。每天都有大量的农产品需要通过网络发布&#xff0c;为此&#xff0c;本人开发了一个基于springboot停车微信小程序小程序。 对于本停车微信小程序的设计来说&#xff0c;它主要是采用后台采用java语…

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

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

PDF有限制密码,不能复制怎么办?

大家现在接触PDF文件越来越多&#xff0c;有的时候在网上下载的PDF文件打开之后&#xff0c;发现选中文字之后无法复制。甚至其他功能也都无法使用&#xff0c;这是怎么回事&#xff1f;该怎么办&#xff1f; 当我们发现文件打开之后&#xff0c;编辑功能无法使用&#xff0c;很…

数据库数据迁移常见方式

数据库数据迁移常见方式 数据库数据迁移常见方式1、通过sql2、通过数据迁移工具3、云服务进行数据迁移什么是DRS服务如何使用DRS服务DRS云服务可以干什么 数据库数据迁移常见方式 1、通过sql 批量导入sql insert into tableName select * 2、通过数据迁移工具 在数据库里面…

19.9 Boost Asio 同步字典传输

这里所代指的字典是Python中的样子&#xff0c;本节内容我们将通过使用Boost中自带的Tokenizer分词器实现对特定字符串的切割功能&#xff0c;使用Boost Tokenizer&#xff0c;可以通过构建一个分隔符或正则表达式的实例来初始化tokenizer。然后&#xff0c;可以使用该实例对输…

网络工程师回顾学习(第一部分)

根据书本目录&#xff0c;写下需要记忆的地方&#xff1a; 参考之前的笔记&#xff1a; 网络工程师回答问题_one day321的博客-CSDN博客 重构第一部分需要记忆的&#xff1a; 第一章&#xff1a;计算机网络概论 计算机网络的定义和分类&#xff1a;计算机网络是指将地理位…

Azure - 机器学习:自动化机器学习中计算机视觉任务的超参数

Azure Machine Learning借助对计算机视觉任务的支持&#xff0c;可以控制模型算法和扫描超参数。 这些模型算法和超参数将作为参数空间传入以进行扫描。 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&#xff0c;同济…

MYSQL函数,一篇文章看完!

做程序员的谁会离得开数据库呢&#xff1f;今天就来分享一下我整理的MySQL的常用函数&#xff0c;基本上囊括了平时要用的函数&#xff0c;它们已经陪我走过了不少年头了&#xff0c;风里来雨里去&#xff0c;缝缝补补又几年&#xff0c;希望能帮到你们&#xff01; 如果数据库…

UltraEdit2024免费版文本编辑器

我们必须承认软件员使用的编辑器或代码编辑器是一款强大 IDE 的重要组成部分&#xff0c;它是任何 IDE 的核心基础。用户量向我们证明了UEStudio 基于著名的 UltraEdit 进行构建&#xff0c;同样&#xff0c;软件的主干非常成熟和稳定&#xff0c;并且已经被证实成为文本和软件…

Python 标准库 subprocess 模块详解

1. Subprocess模块介绍 1.1 基本功能 subprocess 模块&#xff0c;允许生成新的进程执行命令行指令&#xff0c;python程序&#xff0c;以及其它语言编写的应用程序, 如 java, c,rust 应用等。subprocess可连接多个进程的输入、输出、错误管道&#xff0c;并且获取它们的返回…