依赖注入是Java 后端开发的基本技术之一,可帮助您构建满足现代软件需求的弹性且可扩展的应用程序。 DI 用于通过将依赖关系外部化到类本身中来简化依赖关系管理、简化代码维护、促进模块化并增强可测试性。
为什么这项技术对Java 开发人员很重要?它如何有效地解决常见痛点?本文解释了依赖注入的实际好处、基本实践,并且我们将介绍其应用示例。让我们看一下Java 后端应用程序中依赖注入的一些实用策略。
我们需要依赖注入什么?
可测试性(系统可测试的程度)是Java后端开发的一个重要方面,依赖注入在这里至关重要。
假设您有一个从外部数据库检索数据的Java 类。如果没有DI,类本身可能会与数据库连接紧密耦合,这会使单元测试变得复杂。 DI 允许您注入数据库依赖项并简化单元测试期间的模拟。例如,Mockito,一种流行的Java 模拟框架,允许您将模拟DataSource 对象注入到您的类中,从而无需真正的数据库连接即可进行全面测试。
另一个说明性示例是测试与外部Web 服务交互的类。假设您的Java 服务类向第三方API 发出HTTP 请求。通过将模拟HTTP 客户端依赖项注入DI,您可以在单元测试期间模拟来自API 的不同响应,从而实现全面的测试覆盖率。
您还可以在代码库中模拟静态调用,但这很困难,而且在性能方面效率较低。还必须使用PowerMock 等特殊库。此外,静态方法和标记为Final 的类更难模拟。与DI 促进的简化方法相比,这种复杂性降低了单元测试的敏捷性和有效性。
实现的抽象
实现抽象是构建灵活且可维护的代码库的重要技术。 DI 通过将类与具体实现分离并使接口更易于编程来帮助实现这一目标。
事实上,假设您有一个处理用户数据的Java 服务类。您可以使用DI 来注入验证实用程序依赖项,而不是直接实例化验证实用程序类。例如,您可以定义用于验证的通用接口并在运行时注入不同的验证实现。这使您可以在不同的验证策略之间切换,而无需更改服务类别。让我们用一个简单的例子来说明这个想法。
公共接口验证器{
布尔值isValid(字符串数据);
}
公共类RegexValidator 实现Validator {;
@覆盖
公共布尔isValid(字符串数据){
//基于正则表达式的逻辑
返回真。
}
}
公共类CustomValidator 实现Validator {;
@覆盖
公共布尔isValid(字符串数据){
//自定义逻辑
返回真。
}
}
公共类数据服务{
私人最终验证者验证者。
公共数据服务(验证器){
this.validator=验证器;
}
公共无效过程数据(字符串数据){
if (validator.isValid(data)) {
//处理有效数据
} 除此之外{
//处理无效数据
}
}
}
在这里,该类依赖于一个允许您注入各种验证实现的接口。这种方法允许您轻松地更换不同的验证策略,而无需更改您的类,从而使您的代码更加灵活和可维护。数据服务验证器数据服务
代码的可读性和理解力
DI 擅长的第三个领域是确保代码可读性。
假设您正在查看Java 代码库并遇到具有外部依赖项的类。如果没有DI,这些依赖项可能会在类中紧密耦合,从而导致难以破译代码的逻辑。例如,DI 和构造函数注入允许您在类的构造函数签名中明确依赖项,从而使您的代码更具可读性并简化对其功能的理解。
此外,DI 通过将类与依赖项分离来促进模块化和封装。通过这种方法,每个类都有明确定义的职责,并且可以很容易地理解它自己。此外,DI 鼓励使用接口,通过抽象实现细节和推广基于契约的软件设计方法来进一步提高代码可读性。
这是我第二次提到界面。接口是常见的Java 类,但当与DI 结合使用时,它们成为解耦依赖项并提高代码库灵活性的强大工具。下面,我们将解释如何在代码中实现这种组合以及其他实用见解,以帮助您充分利用DI。
依赖关系注入的最佳实践
使用接口
接口充当契约,定义实现类的预期行为,并允许在不更改客户端代码的情况下实现可互换的实现。如上所述,如果您稍后需要更改一些依赖项(例如,将实现从v1 更改为v2),如果幸运的话,调用者可能不需要更改任何内容。由于这些类依赖于接口而不是实现,因此您可以简单地更改它们的配置以提供一种实际的实现而不是另一种。
例如,假设您有一个需要数据库访问的Java 服务类。通过定义表示数据库访问操作的接口并将其注入到服务类中,您可以将该类与特定的数据库实现隔离。此方法允许您简化数据库提供程序交换(例如,从MySQL 到PostgreSQL),而不影响DataAccess 服务类的功能。
公共接口数据访问{
无效保存数据(字符串数据);
}
公共类MySQLDataAccess 实现DataAccess {
@覆盖
公共无效保存数据(字符串数据){
//保存数据到MySQL
}
}
公共类PostgreSQLDataAccess 实现DataAccess {
@覆盖
公共无效保存数据(字符串数据){
//保存数据到PostgreSQL
}
}
公共类数据服务{
私有最终数据访问数据访问;
公共数据服务(数据访问数据访问){
this.dataAccess=数据访问;
}
公共无效过程数据(字符串数据){
dataAccess.saveData(数据);
}
}
在这里,该类依赖于一个接口,您可以根据需要注入不同的数据库访问实现。数据服务数据访问
使用 DI 包装外部库
将外部库合并到Java 后端可能会由于紧密耦合而导致可测试性难以维护。 DI 允许您将这些依赖项封装在您自己的抽象中。
想象一个Java 类需要外部库的功能,例如加密操作。如果没有DI,类将与该库紧密联系在一起,从而使它们难以测试和适应。 DI 允许您使用接口或抽象层包装外部库。将这种人为依赖项注入到您的类中可以让您在测试期间轻松替换它。
公共接口CryptoService {
字符串加密(字符串数据);
}
公共类ExternalCryptoLibrary实现CryptoService {
@覆盖
公共字符串加密(字符串数据){
//使用外部库的加密逻辑
返回加密数据。
}
}
公共类数据处理器{
私有最终CryptoService cryptoService;
公共数据处理器(CryptoService cryptoService){
this.cryptoService=cryptoService;
}
公共字符串processData(字符串数据){
字符串加密数据=cryptoService.encrypt(data);
//附加数据处理逻辑
返回处理后的数据。
}
}
在此示例中,类依赖于接口。在生产环境中,您可以使用这种利用外部库进行加密的实现。但是,在测试过程中,您可以提供接口的模拟实现来模拟加密,而无需调用实际的外部库。数据处理器CryptoService 外部CryptoLibraryCryptoService
明智地使用依赖注入
无论您的DI 技术有多强大,您都不想过度使用它们,因为过度使用它们会使您的代码过于复杂并且在任何时候都不太有用。
假设您需要将一些功能提取到实用程序类中(例如比较两个日期)。如果您的逻辑足够简单并且不太可能改变,那么使用静态方法可能是一个足够的解决方案。在这种情况下,静态实用程序方法简单高效,并且可以在需要时消除DI 开销。
另一方面,如果您正在处理可能在应用程序的生命周期中不断发展的业务逻辑,或者是特定于域的业务逻辑,那么这是依赖项注入的良好候选者。
因此,最终,您应该根据相关功能的性质及其开发目的来决定是否使用DI。是的,当我们谈论灵活性和适应性时,DI 很棒,但传统的静态方法简化了静态、不可变的逻辑。
利用现有的 DI 框架
不要创建自己的DI 框架,而是尝试使用现有的DI 框架。 —— 您只需要知道它是我自己创建的。) 然而,现有框架的好处往往超过从头开始构建解决方案的吸引力。
已建立的框架提供可靠性、可预测性和广泛的文档。这些都通过实际使用进行了改进,保证了项目的稳定性。此外,利用它们可以让您获得丰富的社区知识和支持——,因此选择现有框架可以节省您的时间和精力。因此,虽然重新发明轮子可能很诱人,但如果不这样做,您实际上更有可能简化开发流程并取得更大成功。
以上#JAVA依赖注入相关内容来源仅供参考。相关信息请参见官方公告。
原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/91247.html