JAVA的依赖注入--搞不定的方式

        依赖注入是 Java 后端开发的基础技术之一,有助于构建针对现代软件需求量身定制的弹性和可扩展应用程序。DI 用于通过将依赖项外部化类本身、简化代码维护、促进模块化和增强可测试性来简化依赖项管理。

98c9797bd0246e5d87eb71abff909a39.png

        为什么这种技术对 Java 开发人员至关重要?它如何有效解决常见的痛点?在本文中,我将向您介绍依赖注入的实际好处、基本实践和实际应用。让我们来探讨一下 Java 后端应用程序中依赖注入的实用策略。

我们需要依赖注入什么?

        可测试性(测试系统的程度)是 Java 后端开发的一个关键方面,而依赖注入在这里是必不可少的。

        假设您有一个 Java 类从外部数据库获取数据。如果不使用 DI,则该类可能会将自身紧密耦合到数据库连接,这将使单元测试复杂化。通过使用 DI,您可以注入数据库依赖项,从而简化单元测试期间的模拟。例如,Mockito,一个流行的 Java 模拟框架,将允许您将模拟 DataSource 对象注入到类中,从而促进在没有实际数据库连接的情况下进行全面测试。

        另一个说明性示例是对与外部 Web 服务交互的类的测试。假设 Java 服务类向第三方 API 发出 HTTP 请求。通过向 DI 注入模拟 HTTP 客户端依赖关系,您可以在单元测试期间模拟来自 API 的各种响应,从而实现全面的测试覆盖率。

        代码库中的静态调用也可以被嘲笑,尽管它在性能方面既难以实现又效率较低。您还必须使用专门的库,例如 PowerMock。此外,标记为 final 的静态方法和类的模拟更具挑战性。与 DI 促进的简化方法相比,这种复杂性破坏了单元测试的敏捷性和有效性。

实现的抽象

        实现实现的抽象是构建灵活且可维护的代码库的关键技术。DI 可以通过将类与具体实现分离并促进对接口的编程来帮助您实现此目标。

        实际上,假设您有一个负责处理用户数据的 Java 服务类。您可以使用 DI 注入验证实用程序依赖项,而不是直接实例化验证实用程序类。例如,您可以定义用于验证的通用接口,并在运行时注入不同的验证实现。这样,您将能够在不同的验证策略之间切换,而无需修改服务类。让我用一个简单的例子来说明这个想法:

public interface Validator {
    boolean isValid(String data);
}

public class RegexValidator implements Validator {
    @Override
    public boolean isValid(String data) {
        // Regular expression-based logic
        return true;
    }
}

public class CustomValidator implements Validator {
    @Override
    public boolean isValid(String data) {
        // Custom logic
        return true;
    }
}

public class DataService {
    private final Validator validator;

    public DataService(Validator validator) {
        this.validator = validator;
    }

    public void processData(String data) {
        if (validator.isValid(data)) {
            // Processing valid data
        } else {
            // Handling invalid data
        }
    }
}

        在这里,该类依赖于一个接口,允许注入不同的验证实现。这种方法使代码更加灵活和可维护,因为无需修改类即可轻松交换不同的验证策略。DataServiceValidatorDataService

代码的可读性和理解力

        DI 闪耀的第三个领域是确保代码的可读性。

        假设在 Java 代码库审查期间,您遇到了一个具有外部依赖关系的类。如果没有 DI,这些依赖项可能会在类中紧密耦合,这使得破译代码的逻辑变得具有挑战性。例如,使用 DI 和构造函数注入,可以在类的构造函数签名中显式显示依赖项,从而增强代码的可读性并简化对其功能的理解。

        此外,DI 通过将类与其依赖项解耦来促进模块化和封装。通过这种方法,每个班级都有明确定义的职责,可以很容易地孤立地理解。此外,DI 鼓励使用接口,通过抽象实现细节和促进基于契约的软件设计方法进一步增强代码可读性。

        这是我第二次提到接口。接口是一个常见的 Java 类,但与 DI 结合使用时,它可作为解耦依赖关系和提高代码库灵活性的强大工具。下面,我将讨论如何在代码中实现此组合 - 以及其他可帮助您充分利用 DI 的实用见解。

依赖关系注入的最佳实践

使用接口

        接口充当定义实现类预期行为的协定,允许在不修改客户端代码的情况下实现可互换的实现。正如我上面提到的,如果以后需要对某些依赖项进行更改(例如,将实现从 v1 更改为 v2),那么,如果幸运的话,调用方可能需要零更改。您只需更改配置即可提供一种实际实现而不是另一种实现;由于这些类依赖于接口而不是实现,因此它们不需要任何更改。

        例如,假设您有一个需要数据库访问的 Java 服务类。通过定义表示数据库访问操作的接口并将其注入到服务类中,可以将该类与特定数据库实现分离。使用此方法,可以简化数据库提供程序的交换(例如,从 MySQL 到 PostgreSQL),而不会影响服务类的功能:DataAccess

public interface DataAccess {
    void saveData(String data);
}

public class MySQLDataAccess implements DataAccess {
    @Override
    public void saveData(String data) {
        // Saving data to MySQL
    }
}

public class PostgreSQLDataAccess implements DataAccess {
    @Override
    public void saveData(String data) {
        // Saving data to PostgreSQL
    }
}

public class DataService {
    private final DataAccess dataAccess;

    public DataService(DataAccess dataAccess) {
        this.dataAccess = dataAccess;
    }

    public void processData(String data) {
        dataAccess.saveData(data);
    }
}

        在这里,该类依赖于接口,允许根据需要注入不同的数据库访问实现。DataServiceDataAccess

使用 DI 包装外部库

        由于紧密耦合,将外部库合并到 Java 后端可能会使维护可测试性成为一项挑战。DI 使您能够将这些依赖项封装在自己的抽象中。

        想象一下,您的 Java 类需要外部库的功能,例如加密操作。如果没有 DI,您的班级将与此库紧密联系在一起,从而使测试和适应性变得困难。通过 DI,您可以将外部库包装在接口或抽象层中。这种人为依赖关系随后可以注入到您的类中,从而在测试期间轻松替换:

public interface CryptoService {
    String encrypt(String data);
}

public class ExternalCryptoLibrary implements CryptoService {
    @Override
    public String encrypt(String data) {
        // Encryption logic using the external library
        return encryptedData;
    }
}

public class DataProcessor {
    private final CryptoService cryptoService;

    public DataProcessor(CryptoService cryptoService) {
        this.cryptoService = cryptoService;
    }

    public String processData(String data) {
        String encryptedData = cryptoService.encrypt(data);
        // Additional data processing logic
        return processedData;
    }
}

        在此示例中,该类依赖于接口。在生产过程中,您可以使用该实现,该实现利用外部库进行加密。但是,在测试期间,您可以提供接口的模拟实现,在不调用实际外部库的情况下模拟加密。DataProcessorCryptoServiceExternalCryptoLibraryCryptoService

明智地使用依赖注入

        无论 DI 技术多么强大,您都不想过度使用它;它的过度使用可能会使你的代码在何时何地变得过于复杂,甚至没有多大帮助。

        比方说,您需要将一些功能提取到实用程序类中(例如,比较两个日期)。如果逻辑足够简单并且不太可能改变,那么使用静态方法将是一个足够的解决方案。在这种情况下,静态实用方法简单而高效,无需必要时即可消除 DI 的开销。

        另一方面,如果你处理的业务逻辑可以在应用的生命周期内发展,或者是与领域相关的,那么这是依赖注入的绝佳候选者。

        因此,最终,您应该根据相关功能的性质及其预期开发来决定是否使用 DI。是的,当我们谈论灵活性和适应性时,DI 大放异彩,但传统的静态方法为静态和不变逻辑提供了简单性。

利用现有的 DI 框架

        尝试使用现有的 DI 框架而不是构建自己的框架,即使创建一个框架可能很诱人——我应该知道,我自己也做了一个!;)但是,现有框架的优势往往超过从头开始构建解决方案的吸引力。

        已建立的框架提供可靠性、可预测性和广泛的文档。它们已通过实际使用进行了改进,确保了项目的稳定性。此外,利用它们可以让您获得丰富的社区知识和支持——因此,选择现有框架可以节省时间和精力。因此,虽然重新发明轮子可能很诱人,但实际上不这样做可以简化您的开发过程并为您的成功做好准备。

  • 28
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晨曦_子画

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值