Java中的依赖注入(Dependency Injection, DI)详解

news2024/11/26 2:39:09

Java中的依赖注入(Dependency Injection, DI)是软件工程中的一种重要设计模式。它有助于提高系统的可测试性、可维护性和灵活性。通过依赖注入,组件不再负责创建它们所需的对象,而是通过外部的设置来提供这些对象。这种方式也与控制反转(Inversion of Control, IoC)紧密相关,IoC是一个更大的概念框架,而依赖注入是实现IoC的一种方式。本篇文章将详细探讨Java中的依赖注入机制,并通过具体实例进行全方位分析。
在这里插入图片描述

依赖注入的基本概念

在这里插入图片描述

在面向对象编程中,对象通常依赖于其他对象来进行合作。例如,一个“订单处理”对象可能依赖于“支付服务”对象和“库存服务”对象。在传统的编程模型下,这些依赖关系往往通过在代码中直接创建对象实现:

public class OrderProcessor {
    private PaymentService paymentService;
    private InventoryService inventoryService;

    public OrderProcessor() {
        this.paymentService = new PaymentService();
        this.inventoryService = new InventoryService();
    }

    public void processOrder(Order order) {
        // 使用 paymentService 和 inventoryService
    }
}

这种方式存在的问题是:耦合度高,难以测试。例如,如果我们想为OrderProcessor编写一个单元测试,但不希望测试代码实际调用PaymentServiceInventoryService,就会很困难。

依赖注入则提供了一种方式来解决这个问题。基本思路是将对象所依赖的实例从外部传递进来,而不是在内部自己创建。在Java中,依赖注入可以通过构造器注入、字段注入或方法注入来实现。

依赖注入的三种方式

在这里插入图片描述

  1. 构造器注入(Constructor Injection)
    在这里插入图片描述

    在构造器注入中,依赖是通过类的构造函数传递的。构造器注入确保了对象在被构建时完全初始化,确保依赖不可变:

    public class OrderProcessor {
        private final PaymentService paymentService;
        private final InventoryService inventoryService;
        
        public OrderProcessor(PaymentService paymentService, InventoryService inventoryService) {
            this.paymentService = paymentService;
            this.inventoryService = inventoryService;
        }
    
        public void processOrder(Order order) {
            // 使用 paymentService 和 inventoryService
        }
    }
    
  2. 字段注入(Field Injection)
    在这里插入图片描述

    字段注入使用反射来注入对象的私有成员。通常字段上会有注解如@Inject来表明这些字段需要注入。尽管字段注入减少了样板代码,但使得依赖在对象构造时不明显:

    public class OrderProcessor {
        @Inject
        private PaymentService paymentService;
        
        @Inject
        private InventoryService inventoryService;
    
        public void processOrder(Order order) {
            // 使用 paymentService 和 inventoryService
        }
    }
    
  3. 方法注入(Setter Injection)
    在这里插入图片描述

    方法注入允许通过Setter方法来注入依赖。Setter 是否需要公开是一个需要考虑的问题,因为它会在类的接口中暴露依赖设置能力:

    public class OrderProcessor {
        private PaymentService paymentService;
        private InventoryService inventoryService;
    
        @Inject
        public void setPaymentService(PaymentService paymentService) {
            this.paymentService = paymentService;
        }
    
        @Inject
        public void setInventoryService(InventoryService inventoryService) {
            this.inventoryService = inventoryService;
        }
    
        public void processOrder(Order order) {
            // 使用 paymentService 和 inventoryService
        }
    }
    

框架支持

在这里插入图片描述

Java依赖注入通常通过框架来实现,最流行的DI框架包括Spring和Google Guice。

  • Spring DI

    Spring是Java中最流行的DI框架之一。它可以使用XML、Java注解或Java配置类来定义Beans及其之间的依赖关系。

    <!-- XML配置 -->
    <bean id="paymentService" class="com.example.PaymentService"/>
    <bean id="inventoryService" class="com.example.InventoryService"/>
    <bean id="orderProcessor" class="com.example.OrderProcessor">
        <constructor-arg ref="paymentService"/>
        <constructor-arg ref="inventoryService"/>
    </bean>
    

    现代Spring应用更倾向于使用Java注解配置:

    @Configuration
    public class AppConfig {
        
        @Bean
        public PaymentService paymentService() {
            return new PaymentService();
        }
    
        @Bean
        public InventoryService inventoryService() {
            return new InventoryService();
        }
    
        @Bean
        public OrderProcessor orderProcessor() {
            return new OrderProcessor(paymentService(), inventoryService());
        }
    }
    
  • Google Guice

    Guice是Google推荐的DI框架,强调纯Java代码而不是XML配置。Guice使用模块(Module)来配置注入规则:

    public class BillingModule extends AbstractModule {
        @Override
        protected void configure() {
            bind(PaymentService.class).to(PaymentServiceImpl.class);
            bind(InventoryService.class).to(InventoryService.class);
        }
    }
    
    Injector injector = Guice.createInjector(new BillingModule());
    OrderProcessor orderProcessor = injector.getInstance(OrderProcessor.class);
    

依赖注入的优点和缺点

优点:

  1. 提高可测试性:通过注入依赖,我们可以轻松地为类提供模拟对象(Mocks),从而进行单元测试。

  2. 降低耦合度:类依赖于接口而不是具体实现,促进了松耦合的设计。

  3. 增强灵活性和可维护性:更新对象的实现或改变构造对象的方式时,无需更改类的内部逻辑。

缺点:

  1. 复杂性:DI引入了额外的抽象层,增加了项目的结构复杂性。

  2. 初始化过程不透明性:当应用的依赖链变得复杂时,跟踪对象初始化过程比较困难。

  3. 性能开销:尽管现代DI框架在性能方面进行了许多优化,但由于依赖注入一定程度上会增加应用的启动时间和内存消耗。

实例分析

为了更好理解依赖注入的应用,下面通过一个例子来说明如何利用Spring DI实现基本的依赖注入。

假设我们构建一个简单的应用程序,它使用不同的支付方式来处理订单。首先,我们定义了支付服务接口:

public interface PaymentService {
    void pay(double amount);
}

然后实现两个具体的支付方式:

public class CreditCardPaymentService implements PaymentService {
    public void pay(double amount) {
        System.out.println("Processing credit card payment of " + amount);
    }
}

public class PayPalPaymentService implements PaymentService {
    public void pay(double amount) {
        System.out.println("Processing PayPal payment of " + amount);
    }
}

接着,我们创建一个订单处理类,该类依赖一个支付服务:

public class OrderProcessor {
    private final PaymentService paymentService;

    public OrderProcessor(PaymentService paymentService) {
        this.paymentService = paymentService;
    }

    public void process(double amount) {
        paymentService.pay(amount);
    }
}

最后,我们使用Spring配置将具体的支付服务注入到OrderProcessor中:

@Configuration
public class AppConfig {

    @Bean
    public PaymentService paymentService() {
        return new PayPalPaymentService(); // 可以轻松切换为new CreditCardPaymentService()
    }

    @Bean
    public OrderProcessor orderProcessor() {
        return new OrderProcessor(paymentService());
    }
}

通过这种配置,OrderProcessor依赖的支付服务可通过简单的配置更改以实现不同的付款方式,而不需要修改OrderProcessor类本身。

总结

Java中的依赖注入作为一种设计模式,可以有效提升软件系统的模块化和可维护性。通过将依赖的管理与对象本身的逻辑实现分离,开发者可以创建更松散耦合、更灵活的应用程序设计。无论是小型项目还是大型企业级应用,DI都在现代软件开发中发挥了至关重要的作用。通过细致了解并掌握依赖注入,可以大幅度提高Java应用的设计质量和开发效率。
在这里插入图片描述

//python 因为爱,所以学
print("Hello, Python!")

关注我,不迷路,共学习,同进步

关注我,不迷路,共学习,同进步

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

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

相关文章

无人机+无人车+机器狗:综合管控系统技术详解

无人机、无人车、机器狗的综合管控系统技术是一个集成了多种先进技术和设备的复杂系统&#xff0c;旨在实现高效、精准、协同的作业与管理。以下是对该系统技术的详细解析&#xff1a; 一、系统概述 综合管控系统通过集成无人机、无人车和机器狗等智能设备&#xff0c;结合物…

OSDU轻量化单机部署

首先更新系统 sudo apt update sudo apt upgrade -y安装docker sudo apt install -y docker.io sudo systemctl start docker sudo systemctl enable docker安装minikube curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 …

vmware Workstation16设置批量虚拟机开机自启 vmAutoStart

文章目录 前言解压压缩包一、使用步骤1.获取虚拟机所在目录2.获取vmware所在目录3.测试启动4.开机自启 二、gitee总结 前言 vmware workstation16不支持虚拟机开机自启&#xff0c;通常的办法是写脚本&#xff0c;但是有个问题就是不能启动多台虚拟机&#xff0c;因为有时候会…

Python | Leetcode Python题解之第455题分发饼干

题目&#xff1a; 题解&#xff1a; class Solution:def findContentChildren(self, g: List[int], s: List[int]) -> int:g.sort()s.sort()m, n len(g), len(s)i j count 0while i < m and j < n:while j < n and g[i] > s[j]:j 1if j < n:count 1i …

Spring框架使用Api接口实现AOP的切面编程、两种方式的程序示例以及Java各数据类型及基本数据类型的默认值/最大值/最小值列表

一、Spring框架使用Api接口-继承类实现AOP的切面编程示例 要使用Spring框架AOP&#xff0c;除了要导入spring框架包外&#xff0c;还需要导入一个织入的包org.aspectj&#xff0c;具体maven依赖如下&#xff1a; <dependency><groupId>org.springframework</gr…

JAVA-异常(通俗易懂)

目录 一、异常的概念 1.算术异常 2.数组越界异常 3.空指针异常 二、异常体系结构 三、异常的分类 1. 编译时异常 2. 运行时异常 四、异常处理 1.防御式编程 2.异常的抛出 3.异常的捕获 4.try-catch捕获并处理 5.finally 五、异常的处理流程 六. 自定义异常类…

ade20k 街景图像【数据集】及其【论文出处】ADE20K数据集 超过25000张图像的语义分割数据集

ade20k 街景图像【数据集】及其【论文出处】ADE20K数据集介绍 是一个包含超过25000张图像的语义分割数据集&#xff0c;这些图像被密集注释&#xff0c;覆盖室内和室外场景。 它由MIT发布&#xff0c;包含100个事物类别和50个物质类别&#xff0c; 用于训练和验证的图像数量分别…

(16)MATLAB仿真Nakagami-m分布1

文章目录 前言一、Nakagami分布二、MATLAB建模代码三、仿真结果画图四、总结 前言 Nakagami衰落模型最初是由于该模型与短波电离层传播的经验结果相匹配而提出的。它还用于仿真来自多个干扰源的情况&#xff0c;因为多个独立且同分布&#xff08;i.i.d&#xff09;的瑞利分布随…

线程池的实现和讲解:解决多线程并发服务器创建销毁线程消耗过大的问题

1.前言 多进程/线程并发服务器、多路I/O转接服务器的简单实现-CSDN博客 原先的多线程并发服务器&#xff0c;有多少个客户端连接服务器就有多少个线程&#xff0c;CPU需要在多个线程之间来回切换处理客户端的请求&#xff0c;系统消耗比较大(每次创建和消耗线程在操作系统内部…

linux学习--第七天(多路复用IO)

多路复用IO -阻塞IO与非阻塞IO -IO模型 IO的本质时基于操作系统接口来控制底层的硬件之间数据传输&#xff0c;并且在操作系统中实现了多种不同的IO方式&#xff08;模型&#xff09;比较常见的有下列三种&#xff1a; 1.阻塞型IO模型 2.非阻塞型IO模型 3.多路复用IO模型 -阻…

开源2+1链动模式AI智能名片O2O商城小程序源码:线下店立体连接的超强助力器

摘要&#xff1a;本文将为您揭示线下店立体连接的重大意义&#xff0c;您知道吗&#xff1f;线上越火&#xff0c;线下就得越深入经营。现代门店可不再只是卖东西的地儿&#xff0c;还得连接KOC呢&#xff01;咱们来看看门店要做的那些超重要的事儿&#xff0c;还有开源21链动模…

Authentication Lab | CVE-2019-7644 - JWT Signature Disclosure

关注这个靶场的其他相关笔记&#xff1a;Authentication Lab —— 靶场笔记合集-CSDN博客 0x01&#xff1a;JWT Signature Disclosure 前情提要 本关的考点是 JWT&#xff08;Json Web Token&#xff09;漏洞&#xff0c;JWT 是一个用于跨域认证的技术。如果你不了解 JWT&…

计算机视觉——图像修复综述篇

目录 1. Deterministic Image Inpainting 判别器图像修复 1.1. sigle-shot framework (1) Generators (2) training objects / Loss Functions 1.2. two-stage framework 2. Stochastic Image Inpainting 随机图像修复 2.1. VAE-based methods 2.2. GAN-based methods …

攻防世界----->easyre-153

做题笔记。 下载 查壳。 UPX&#xff0c;---脱壳。 32ida打开。 先运行一下&#xff1a; 查找字符校位。 管道父子&#xff1f;有点像此前做的那个进程互斥。。。 分析&#xff1a; 跟进lol &#xff1f; 查看汇编窗口看看。(因为一个函数只存在一个打印函数&#xff0c;就很…

集合框架01:集合的概念、Collection体系、Collection接口

1.集合的概念 集合是对象的容器&#xff0c;定义了多个对象进行操作的常用方法。可实现数组的功能。 集合和数组的区别&#xff1a; 1.数组长度固定&#xff0c;集合长度不固定&#xff1b; 2.数组可以存储基本类型和引用类型&#xff0c;集合只能存储引用类型&#xff1b; …

读数据湖仓06数据集成

1. 数据湖仓中的数据集成 1.1. 数据湖仓的总体目标是为每一个人提供支持&#xff0c;包括从普通职员到CEO 1.2. 有了作为基础设施的基础数据&#xff0c;企业等组织才能实现真正的数据驱动 1.3. 提供组织所需的数据&#xff0c;最关键的一环在于提供集成的数据基础 1.3.1. 只…

信息安全工程师(32)认证技术方法

前言 认证技术方法是用于验证用户、设备或系统身份的各种技术手段和方法&#xff0c;旨在确保只有经过验证的实体才能访问系统资源&#xff0c;从而保护数据和系统的安全。 一、常见认证技术方法 密码认证 描述&#xff1a;用户通过输入预先设置的密码进行身份验证。优点&#…

The 14th Jilin Provincial Collegiate Programming Contest

题目 #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 #define ll long long #define pii pair<int, int> #define ld lo…

C语言 | Leetcode C语言题解之第455题分发饼干

题目&#xff1a; 题解&#xff1a; int cmp(int* a, int* b) {return *a - *b; }int findContentChildren(int* g, int gSize, int* s, int sSize) {qsort(g, gSize, sizeof(int), cmp);qsort(s, sSize, sizeof(int), cmp);int m gSize, n sSize;int count 0;for (int i …

D26【python 接口自动化学习】- python 基础之判断与循环

day26 语句嵌套 学习日期&#xff1a;20241003 学习目标&#xff1a;判断与循环&#xfe63;-36 语句嵌套&#xff1a;如何处理多重嵌套的问题&#xff1f; 学习笔记&#xff1a; 语句嵌套的用途 在条件语句中使用另外一个条件语句 在循环中使用条件语句 多重循环 总结 1…