Spring从零开始学使用系列(三)--依赖注入(DI)

目录

1.DI的核心概念

1.1优势

2. Spring中的DI实现

2.1 构造器注入

2.1.2 优势和缺点

2.2 设置器注入

 2.2.1 如何使用设置器注入

2.2.2 示例代码

2.2.3优势和使用场景

 2.3 字段注入

2.4 方法注入

2.4.1 方法注入的概念

2.4.2 找方法注入

2.4.3 @Lookup 注解的作用

2.4.4 结论和最佳实践

2.5 总结

3.Spring事务管理概述

3.1 声明式事务管理

3.1.1 使用@Transactional注解

3.1.2 @Transactional注解的参数解释

3.1.3 Spring Data JPA 和事务

3.2 事务传播行为

3.3事务失效的场景

4.总结


1.DI的核心概念

        依赖注入(DI)是一种设计模式,用于实现对象间的依赖关系的自动满足。在传统的编程模式中,每个对象负责管理其依赖项的创建和绑定,而在使用DI的模式中,这些依赖项是由外部系统(在Spring中是IoC容器)在创建对象时自动注入的。

1.1优势
  • 降低耦合度:对象不需要知道如何创建它们所需的依赖对象。
  • 增强模块化:便于管理大型应用中的依赖关系。
  • 提高可测试性:依赖可以在测试中被替换或模拟。
2. Spring中的DI实现

        Spring提供了多种方式来实现DI,包括构造器注入、设置器注入和字段注入。

2.1 构造器注入

        通过构造器注入,依赖项在对象创建时通过构造函数传入。这是推荐的注入方式,因为它可以保证所需的依赖项在使用前就被正确提供。

@Component
public class ProductService {private final InventoryService inventoryService;public ProductService(InventoryService inventoryService) {this.inventoryService = inventoryService;}
}

        在Spring 4.3之后,如果目标组件只定义了一个构造器,Spring会自动将其作为用于依赖注入的构造器,无需显式使用@Autowired注解。然而,在更早的版本中或者当存在多个构造器时,@Autowired是必须的,以标识哪个构造器应该被用来自动注入。

2.1.2 优势和缺点

优点

  • 依赖不可变,确保所需的依赖在使用前已经提供。
  • 便于发现缺失的依赖,因为在对象创建时就会出错。
  • 最适合用于必需依赖。

缺点

  • 如果依赖项很多,构造器可能会显得笨重。
  • 对于可选依赖不是很灵活。
2.2 设置器注入

        设置器注入,顾名思义,是通过类的设置方法(setter方法)来注入依赖。这种方式的主要特点是它不强制在构造对象时立即提供依赖,而是允许在对象构造后、使用前的任何时间点注入依赖。这提供了更大的灵活性,尤其是在需要重新配置或更换依赖的场景中。

 2.2.1 如何使用设置器注入

        在Spring中使用设置器注入非常简单。首先,你需要在你的类中提供一个公开的设置方法,然后使用@Autowired注解标注这个方法。Spring容器在创建这个类的Bean时,会自动调用这个设置方法,将相应的依赖注入进去。

2.2.2 示例代码

        假设你有一个ProductService类,它依赖于一个名为InventoryService的服务。下面是如何通过设置器注入来注入这个依赖:

        

@Component
public class ProductService {private InventoryService inventoryService;// 使用@Autowired注解标注设置方法@Autowiredpublic void setInventoryService(InventoryService inventoryService) {this.inventoryService = inventoryService;}public void processProduct() {// 使用inventoryService进行一些操作}
}
2.2.3优势和使用场景

优势:

  • 灵活性:可以在对象的生命周期中的任何时点注入依赖,甚至可以重新注入不同的依赖。
  • 适合可选依赖:如果某个依赖是可选的,使用设置器注入可以避免在构造函数中处理复杂的逻辑。
  • 易于重新配置:适用于那些可能需要重新配置的组件。

使用场景:

  • 当依赖项可能在运行时改变时,例如基于某些业务规则更换策略对象。
  • 在需要通过配置来调整依赖关系的企业应用中,设置器注入提供了一种简单的方式来调整这些依赖。
  • 当某些依赖项是可选的,不是必需的,这时使用设置器注入可以避免在对象创建时就必须提供这些依赖。

潜在的缺点

  • 可能的不完整状态:如果在依赖注入之前就使用了对象,那么可能会导致运行时错误。
  • 过度使用可能导致代码混乱:如果一个类有很多通过设置器注入的依赖,这可能会使类变得难以管理和理解。
 2.3 字段注入

        定义:字段注入是最简单的注入方式,直接在需要的字段上标注@Autowired。这个也是我们最常见的使用,不过现在如果使用的是新的版本的话,idea更支持的是构造器注入了

代码示例

@Component
public class ProductService {@Autowiredprivate InventoryService inventoryService;
}

优点

  • 写法简单,减少了样板代码。

缺点

  • 测试困难,因为没有公开的方法可以用来替换依赖。
  • 可能导致代码难以追踪,特别是在存在多个依赖的情况下。
2.4 方法注入
2.4.1 方法注入的概念

        方法注入主要用于当一个单例Bean需要引用一个原型Bean的实例时。由于单例Bean在Spring容器的生命周期内只创建一次,其依赖的原型Bean也只会注入一次。这就导致了单例Bean不能再次获取原型Bean的新实例,除非使用方法注入。

        Spring提供了一种方法注入的机制来解决这个问题。它主要通过两种方式实现:

  1. 查找方法注入:使用@Lookup注解来标注一个方法,这告诉Spring每次调用这个方法时都要在容器中查找并返回新的Bean实例。
  2. 方法替换:使用replaced-method元素来动态替换方法的实现。
2.4.2 找方法注入
@Component
public abstract class ProductService {public void processProduct() {PricingService pricingService = createPricingService();// 对pricingService执行一些操作}@Lookupprotected abstract PricingService createPricingService();
}

        这里的 @Lookup 注解标注在 createPricingService() 方法上。Spring在运行时将动态生成 ProductService 的一个子类,并覆盖这个方法以从Spring容器中返回PricingService的新实例。

2.4.3 @Lookup 注解的作用

   @Lookup注解告诉Spring容器在每次调用该方法时,都应从Spring IoC容器中获取指定Bean的新实例。这对于处理原型依赖于单例Bean的情况非常有用。

2.4.4 结论和最佳实践

        方法注入特别适合于需要将设计模式如工厂方法模式整合到Spring管理的Bean中的场景。虽然方法注入在某些特定场景下非常有用,但应当谨慎使用,因为它增加了配置的复杂性并可能对性能产生影响。

2.5 总结

       尽管字段注入在某些情况下看似方便,但从长远来看,它可能会引入多种问题,特别是在大型项目中。  构造器注入是推荐的方式,特别是对于必需依赖,因为它能够确保依赖在使用前被正确设置。这也是最符合依赖注入原理的方法,因为它支持不可变性并且确保了对象始终处于有效状态。

3.Spring事务管理概述

         Spring的事务管理支持提供了一致的编程模型,既适用于声明式事务管理也适用于编程式事务管理。它与底层的事务基础设施进行了良好的抽象,使得开发者可以不用关心具体的事务管理API,就能实现跨不同事务管理API的事务操作。

3.1 声明式事务管理

        声明式事务管理是Spring推荐的事务管理方式。它将事务管理代码从业务代码中分离出来,通过配置方式来管理事务。这种方式使用@Transactional注解,非常简单而且高效。

3.1.1 使用@Transactional注解

        在Spring中,你可以通过在类或方法上添加@Transactional注解来声明一个事务。这个注解可以告诉Spring哪些方法是事务性的,以及这些事务应该如何被管理。

@Service
public class ProductService {@Transactionalpublic void updateProductStock(Long productId, int newStock) {// 业务逻辑代码,例如更新库存// 这个方法中的所有数据库操作都会在同一个事务中执行}
}
3.1.2 @Transactional注解的参数解释
  1. propagation:定义了事务的传播行为。这决定了事务方法是如何相互作用的。例如,Propagation.REQUIRED表示当前方法必须在一个具有事务的上下文中执行,如果当前没有事务上下文,就会启动一个新的事务。

  2. isolation:定义了事务的隔离级别,这是一个关键设置,因为它防止了多个事务同时执行时可能产生的问题,如脏读、不可重复读和幻读。常用的隔离级别包括 READ_COMMITTEDREPEATABLE_READ 等。

  3. timeout:定义了事务在被回滚前可以运行的时间(秒)。如果事务超过这个时间还没有完成,就会自动回滚。这有助于防止长时间运行的事务占用资源。

  4. readOnly:标记事务是否为只读事务。设置为true可以帮助数据库优化事务。对于只查询数据的事务,设置为只读可以提高性能。

  5. rollbackFor:定义了哪些异常类会触发事务回滚。默认情况下,事务只对RuntimeExceptionError进行回滚。如果业务逻辑中检测到其他异常也需要回滚事务,需要显式设置。

  6. noRollbackFor:定义了哪些异常类不会触发事务回滚。这在特定情况下非常有用,比如当某些异常不应该导致事务失败时。

3.1.3 Spring Data JPA 和事务

        `Spring Data JPA简化了基于JPA的数据访问层的开发。事务管理是这一模块的核心组成部分,因为任何JPA操作几乎都需要在事务的上下文中执行。

事务的关键应用:

  • Repository层事务Spring Data JPA的Repository接口默认就支持声明式事务。通常不需要手动配置,但可以通过@Transactional注解自定义事务行为。这里很需要注意
  • 复杂查询:对于执行复杂的数据操作或查询,合适的事务管理确保了在执行过程中数据的一致性和完整性。
3.2 事务传播行为

        事务传播行为定义了一个事务性方法如何相对于调用它的方法事务进行交互。Spring定义了几种传播行为,以下是最常用的几种:

  1. REQUIRED(默认): 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  2. REQUIRES_NEW: 创建一个新事务,如果当前存在事务,则暂停当前事务。
  3. SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。
  4. NOT_SUPPORTED: 以非事务方式执行操作,如果当前存在事务,则暂停当前事务。
  5. MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  6. NEVER: 以非事务方式执行,如果当前存在事务,则抛出异常。
  7. NESTED: 如果当前存在事务,则执行一个嵌套的子事务;如果当前没有事务,则表现如REQUIRED
3.3事务失效的场景

        Spring事务管理虽然强大,但在某些情况下可能会失效,导致事务不生效。以下是一些常见的场景:

  1. 私有方法调用:在同一个类中,一个非事务性的公共方法内部调用了一个标注为@Transactional的私有方法,事务是不会生效的,因为Spring事务是通过代理实现的,只能应用于公共接口方法。

  2. 自调用问题:一个类中的一个方法调用另一个标注为@Transactional的方法,如果是通过this方法调用的,事务是不会启动的,因为事务的开启需要通过代理对象来实现。

  3. 异常类型不正确@Transactional注解中可以指定一个异常类型,事务只有在抛出指定异常时才会回滚。如果抛出的异常类型不是事务注解中配置的回滚异常,事务也不会回滚。

  4. 数据库支持不足:如果数据库或数据库驱动不支持事务或当前隔离级别,那么标注了@Transactional的方法也无法正确管理事务。

  5. 方法是非公开的:如前所述,由于Spring的事务管理是通过AOP代理实现的,只有目标方法是公开的接口方法时,代理才能管理其事务。

4.总结

        依赖注入是Spring框架中一个核心的功能,它不仅提高了代码的可维护性和灵活性,还极大地简化了企业级应用的开发。理解和正确使用DI可以帮助开发者构建出更加健壮、可测试且易于维护的Java应用程序。

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

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

相关文章

nosql数据库 redis

一、介绍 1、redis与mysql的区别: Redis是一种基于键值对的内存数据库,数据存储在内存中,因此读写速度非常快。它支持多种数据结构,如字符串、哈希、列表等。 MySQL是一种关系型数据库,数据以表格的形式组织存储在磁…

【Python】使用Pandas和随机森林对鸢尾花数据集进行分类

我在鼓楼的夜色中 为你唱花香自来 在别处 沉默相遇和期待 飞机飞过 车水马龙的城市 千里之外 不离开 把所有的春天 都揉进了一个清晨 把所有停不下的言语变成秘密 关上了门 莫名的情愫啊 请问 谁来将它带走呢 只好把岁月化成歌 留在山河 🎵 鹿…

鸿蒙原生应用元服务-访问控制(权限)开发应用权限列表二

ohos.permission.ACCELEROMETER 允许应用读取加速度传感器的数据。 权限级别 :normal 授权方式 :system_grant ACL使能 :TRUE ohos.permission.GYROSCOPE 允许应用读取陀螺仪传感器的数据。 权限级别 :normal 授权方式 &a…

探索 Python 的动态类型系统:变量引用、不可变性及高效内存管理与垃圾回收机制的深入分析

文章目录 1. 动态类型及其内存管理解析1.1 变量与对象的引用关系1.2 对象的不可变性和内存地址的变化 2. 垃圾回收与内存优化策略2.1 动态内存分配的基础2.2 Python 的垃圾回收 Python作为一种流行的高级编程语言,以其代码的易读性和简洁性著称。尤其是它的动态类型…

泛私域新引擎:小程序AI智能名片S2B2C商城的分销式导购策略与案例剖析

在数字化浪潮的推动下,小程序AI智能名片S2B2C商城以其独特的分销式导购能力,逐渐在泛私域的特殊场景中占据了一席之地。这种模式不仅打破了传统线上线下的界限,更通过以人为核心的营销方式,实现了利益驱动的深度分销。 分销式导购…

Git学习笔记(四)远程仓库

根据前面几篇文章的介绍,在本地使用Git基本不成问题了,常用的基本命令和一些基本概念基本也介绍完毕了。这一张主要讲讲远程仓库的创建和使用。 概念 其实在前面第一篇文章中,我们就简单介绍过远程仓库,它其实就是一个托管在远程服…

yolo-驾驶行为监测:驾驶分心检测-抽烟打电话检测

在现代交通环境中,随着汽车技术的不断进步和智能驾驶辅助系统的普及,驾驶安全成为了公众关注的焦点之一 。 分心驾驶,尤其是抽烟、打电话等行为,是导致交通事故频发的重要因素。为了解决这一问题,研究人员和工程师们…

19-ESP32-S3外设IIC

ESP32-S3的IIC 引言 ESP32-S3是一款集成了Wi-Fi和蓝牙功能的低成本、多功能微控制器。在这篇博客中,我们将详细介绍ESP32-S3的IIC(Inter-Integrated Circuit)接口,也被称为I2C。 IIC简介 IIC是一种串行、同步、多设备、半双工…

Python中, 介绍面向对象及三大特征

面向对象(Object Oriented Programming) OOP, 是一个重要的编程范式, 它主要基于"对象"的概念, 这些对象具有属性(数据)和方法(函数) 三个特征: 封装, 继承,多态 简单来说: 封装: 隐藏内部实现细节, 只保留功能接口 继承: 类与类之间的关系 (比如说父和子的关系…

CTF网络安全大赛详情

网络安全已成为现代社会的一个关键挑战,随着互联网技术的飞速发展,从个人隐私保护到国家安全,网络安全的重要性日益突显。为了应对这一挑战,CTF(Capture The Flag,中文:夺旗赛)应运而…

openEuler-22.03下载、安装

一、下载 下载地址:openEuler下载 | 欧拉系统ISO镜像 | openEuler社区官网 下载版本:openEuler-22.03-LTS-x86_64-dvd.iso 二、安装 配置完后开启虚拟机 设置完后,重启虚拟机 设置桥接模式的网络 cd /etc/sysconfig/network-scripts/ vi if…

LeetCode 2739. 总行驶距离

题目链接https://leetcode.cn/problems/total-distance-traveled/?envTypedaily-question&envId2024-04-25 简单题,看代码思考一下即可理解 class Solution {public int distanceTraveled(int mainTank, int additionalTank) {int res 0;while (mainTank >…

RK3568 学习笔记 : u-boot 千兆网络无法 ping 通PC问题的解决

前言 开发板型号: 【正点原子】 的 RK3568 开发板 使用 虚拟机 ubuntu 20.04 收到单独 编译 RK3568 u-boot 【问题】u-boot 千兆网络无法ping 通?Linux 下千兆网络正常,说明:开发板硬件正常 u-boot 下网络如果通了,…

寄存器和变量有什么区别

2024年4月25日,周四晚上 寄存器和变量是计算机编程和计算机硬件中两个不同的概念,它们在内存管理和数据存储方面有着不同的作用和特点。 寄存器: 硬件实现:寄存器是计算机处理器(CPU)内部的一组高速存储单…

【高频】基于GBDT-FM模型的level-2高频数据实证研究(二)

【高频】基于GBDT-FM模型的level-2高频数据实证研究(二) 原创 Yud. 2AMquant 2024-04-04 11:30 广东 上一篇中初步提及了Level2数据中常见变量指标的构建方式,以及其带来的价格冲击。此篇将使用GBDT-LM模型对短程价格走势进行简单预测。 ps…

Nginx下载安装,什么是nginx,什么是反向代理,Windows下、linux下安装nginx(保姆级教程)

文章目录 一、Nginx简介为什么要使用NginxNginx的特点Nginx的相关概念正向代理反向代理动静分离负载均衡 二、Nginx安装1. Windows安装2. Linux安装 一、Nginx简介 Nginx 是一个高性能的 HTTP(静态资源服务器) 和 反向代理 Web 服务器。 为什么要使用N…

JavaScript常问面试题

css 1.HTML5新增了哪些内容 语义化更好的内容标签(header,footer,main,nav,aside,article,section)音频 ,视频标签(audio, video)画布(canvas)表单控件 calendar , date , time , email , url , search , tel , file , number地理位置API(geolocation)拖拽释放API(Drap and d…

AI-数学-高中-44导数的运算法则

原作者视频:【导数】【一数辞典】3导数的运算法则(略难)_哔哩哔哩_bilibili 三种求导表达方式一样的,中间的比较常用: 链式法则:从外向内:

Redis学习(三)| Redis高可用和容错机制详解

文章目录 高可用性主从复制(Master-Slave Replication)哨兵(Sentinel) 容错性数据持久化哨兵(Sentinel) 高可用vs容错性概念关联 结论 Redis(Remote Dictionary Server)是一种高性能…

如何部署Tensorrtx

因为模型跑起来太慢了,所以想要运用tensorrtx进行加速。但是这个是有难度的,且网络上的教程大多写的不是很好。我将以一个新人的视角,从头开始部署基于yolov5的tensorrtx加速。 知识补充:TensorRT和TensorRTX的区别 tensorRT是英…