当我们在谈论构造函数注入的时候我们在谈论什么 | 京东物流技术团队

news/2024/5/19 2:01:51 标签: 数据库, 服务器, 运维, 构造函数

依赖注入

当涉及依赖注入(Dependency Injection,DI)时,首先推荐使用构造函数注入,因为构造函数注入有很多技术优点,而且还与面向对象的设计原则密切相关。在业界,构造函数注入作为依赖注入的一种最佳实践得到了广泛的认可,在Spring Framework的作者之一Rod Johnson的观点中也得有体现。

下面是Spring官方文档中对于依赖注入的描述:

Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies. Note that use of the @Autowired annotation on a setter method can be used to make the property be a required dependency; however, constructor injection with programmatic validation of arguments is preferable.

The Spring team generally advocates constructor injection, as it lets you implement application components as immutable objects and ensures that required dependencies are not null. Furthermore, constructor-injected components are always returned to the client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.

Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of t hat class amenable to reconfiguration or re-injection later. Management through JMX MBeans is therefore a compelling use case for setter injection. ——Spring官网原文链接 https://docs.spring.io/spring-framework/reference/core/beans/dependencies/factory-collaborators.html

在本文中,我们将更深入地探讨为何构造函数注入被认为是最佳实践,并将通过详细的Java代码示例来阐明其优点。同时,我们将研究如何将构造函数注入与面向对象的设计理念相结合,特别是如何确保封装、单一责任、不变性和依赖倒置原则得以遵循。

有哪些优势

构造函数注入的重要性

依赖注入是一种关键的技术,可以提高应用程序的可测试性和可维护性。Rod Johnson在他的书中明确指出,通过将依赖项注入到对象中,可以更轻松地进行单元测试,同时降低了对象之间的耦合度。这正是构造函数注入所实现的。当我们在对象的构造函数中传递依赖项时,我们不仅提供了明确的依赖关系,还提高了代码的清晰度。让我们通过一个示例来看看构造函数注入的工作方式。

public class OrderService {
    private final OrderRepository orderRepository;

    public OrderService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }
    // ...
}

在上述示例中,OrderService构造函数接受一个OrderRepository作为参数,明确指定了其依赖关系。这不仅提高了代码的可读性,还使得单元测试变得更加容易。您可以轻松地创建一个模拟的OrderRepository并将其传递给OrderService构造函数,以进行单元测试。

构造函数注入与封装特性

面向对象编程强调封装特性,即将数据和行为封装在类的内部,通过公共接口来访问对象。构造函数注入有助于维护封装特性,因为它允许您在对象内部设置依赖项,而不需要向外部暴露setter方法。这符合依赖倒置原则和接口隔离原则的思想。

通过将依赖项作为构造函数参数传递,您确保了依赖项在对象内部得到了封装。这意味着外部代码无法直接修改对象的依赖项,从而提高了代码的安全性和稳定性。让我们来看一个例子:

public class CustomerService {
    private final CustomerRepository customerRepository;
    
    public CustomerService(CustomerRepository customerRepository) {
        this.customerRepository = customerRepository;
    }
    
    public Customer getCustomerById(int customerId) {
        return customerRepository.findById(customerId);
    }
    // ...
}

在上面的示例中,CustomerService依赖于CustomerRepository,并通过构造函数注入的方式获得了该依赖。这确保了customerRepository的封装性,不允许外部代码直接访问或修改它。

构造函数注入与单一责任原则

单一责任原则是面向对象设计的基本原则之一,强调一个类应该只有一个理由去改变。构造函数注入有助于实现这一原则,因为它鼓励每个类专注于执行单一任务,而不负责创建或管理依赖项。通过使用构造函数注入,您可以将依赖项的创建和配置从类中分离出来,使每个类专注于自身的主要职责。这提高了代码的模块化性和可维护性。以下是一个示例:

public class ProductService {
    private final ProductRepository productRepository;

    public ProductService(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    public List<Product> getAllProducts() {
        return productRepository.getAll();
    }

    // ...
}

在上述示例中,ProductService专注于处理产品相关的业务逻辑,而不需要关心如何创建或配置ProductRepository。这遵循了单一责任原则,使代码更加清晰和可维护。

构造函数注入与不变性

构造函数注入还有助于防止不必要的可变性,因为一旦依赖项被设置,它们通常是不可变的。不可变对象在面向对象设计中具有重要地位,因为它们更容易理解和维护。通过将依赖项注入到对象中并在构造函数中进行初始化,您可以确保依赖项在对象的整个生命周期内保持不变。这有助于减少对象状态的变化,从而提高了代码的可维护性和可预测性。

构造函数注入与依赖注入容器

构造函数注入与依赖注入容器(如Spring容器)协同工作得很好。您可以使用构造函数注入来定义组件的依赖关系,并让容器负责创建和管理对象的生命周期。

@Component
public class AppConfig {
    @Bean
    public OrderRepository orderRepository() {
        return new JpaOrderRepository();
    }

    @Bean
    public OrderService orderService(OrderRepository orderRepository) {
        return new OrderService(orderRepository);
    }
}

在上述示例中,我们使用Spring的Java配置来定义OrderRepositoryOrderService之间的依赖关系,并通过构造函数注入实现了依赖解析。

构造函数注入与单元测试

构造函数注入使得编写单元测试变得更容易,因为您可以轻松地传递模拟或测试用的依赖项到对象的构造函数中。这样,您可以在不依赖于容器或其他复杂配置的情况下,对类进行单元测试。

public class OrderServiceTest {
    @Test
    public void testCalculateTotalPrice() {
        OrderRepository mockRepository = mock(OrderRepository.class);
        when(mockRepository.findOrderById(1)).thenReturn(new Order(1, 100.0));

        OrderService orderService = new OrderService(mockRepository);

        double totalPrice = orderService.calculateTotalPrice(1);
        assertEquals(100.0, totalPrice, 0.01);
    }
}

上述单元测试中,我们使用构造函数注入创建了一个OrderService的实例,并注入了一个Mock的OrderRepository

总结

通过以上示例,阐述了构造函数注入在依赖注入中的价值,以及它如何与面向对象的设计原则协同工作。这不仅提高了代码的可维护性和可测试性,还使其更符合面向对象设计的最佳实践。构造函数注入作为一种强大的工具,有助于构建高质量、可维护和可测试的应用程序。希望通过本文,您能更深入地了解构造函数注入的价值和实践。

参考

https://www.baeldung.com/constructor-injection-in-spring

https://docs.spring.io/spring-framework/reference/core/beans/dependencies/factory-collaborators.html

作者:京东物流 张涛

来源:京东云开发者社区 自猿其说Tech 转载请注明来源


http://www.niftyadmin.cn/n/5142392.html

相关文章

家具厂ERP系统有什么牌子?推荐几款适合家具厂的ERP软件

随着企业规模的不断扩大&#xff0c;家具厂的经营管理方式也需要随之转变&#xff0c;进入数字化时代&#xff0c;家具厂ERP系统也进入越来越多企业的视野。 家具厂ERP软件能够协助企业轻松落地全渠道营销策略&#xff0c;自动生成物料BOM&#xff0c;提高数据准确率&#xff…

基于springboot实现学生考勤管理系统【项目源码+论文说明】

基于springboot实现学生考勤管理系统演示 摘要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&am…

个人职业规划

职业规划 软件体系结构 内容 组件 关系 视图 技术 抽象 封装 信息隐藏 模块化 事务分离 耦合和内聚 充分性、完整性和原始性 策略和实现的分离 接口和实现的分离 单一引用点 分而治之 结构 层 管道和过滤器 黑板 系统 分布式系统 代理者 交互式系统 …

服务器基本命令

nohup python3 ./download-all-2023-11-01_12-08-11.py > T85_download.log & 标准输出重定向到文件 nohup bash test.sh > stdout.txt & 标准错误输出重定向到文件 nohup bash test.sh 2> stderr.txt & 重定向到不同文件 nohup bash test.sh > …

集简云slack(自建)无需API开发轻松连接OA、电商、营销、CRM、用户运营、推广、客服等近千款系统

slack是一个工作效率管理平台&#xff0c;让每个人都能够使用无代码自动化和 AI 功能&#xff0c;还可以无缝连接搜索和知识共享&#xff0c;并确保团队保持联系和参与。在世界各地&#xff0c;Slack 不仅受到公司的信任&#xff0c;同时也是人们偏好使用的平台。 官网&#x…

OpenGL ES入门教程(一)编写第一个OpenGL程序

OpenGL ES入门教程&#xff08;一&#xff09;编写第一个OpenGL程序 前言 从本文开始我将参考学习OpenGL ES应用开发实践指南 Android卷 [&#xff08;美&#xff09;KevinBrothaler著]&#xff08;提取码: 394m&#xff09;&#xff0c;并基于自己的理解以更加通俗易懂的方式…

C++ 赋值运算重载,const成员,取地址及const取地址操作符重载

C 赋值运算重载&#xff0c;const成员&#xff0c;取地址及const取地址操作符重载 1. 赋值运算符重载1.1 运算符重载1.2 赋值运算符重载1.3 前置/--和后置/--重载 2. const成员3. 取地址及const取地址操作符重载 所属专栏&#xff1a;C“嘎嘎" 系统学习❤️ &#x1f680;…

echarts 实现分组和显示总数

echart 除了能显示坐标轴外&#xff0c;还可以对坐标轴进行分组&#xff0c;可以更直观的观察数 据&#xff0c;本博文记录一下关于分组的探索&#xff0c;先展示一下效果图&#xff1a; // 直接复制到echarts官方示例中查看效果 let xAxisData []; let data1 []; let data2 …