从 0 开始理解 Spring 的核心思想 —— IoC 和 DI(1)

前言

时光如白驹过隙,一晃笔者又小半个月没更新了,也许无人在意笔者为何没更新了,但笔者还是想通报一下, 笔者这段时间一方面在备战期末考试,另一方面也在梳理学习笔记。

现在,决定开启 Spring 系列博客的正式旅程。

本篇将聚焦 Spring 的核心思想之一 ------ IoC(控制反转)与 DI(依赖注入)。希望与大家一同深入探讨,让 Spring 的"魔法"不再遥不可及。

为什么我们需要Spring?

在深入学习Spring的IoC(控制反转)和DI(依赖注入)之前,让我们先来看看一个简单的对比,你会立刻感受到Spring的魅力。

传统方式 vs Spring方式

传统的Java开发方式:

java 复制代码
public class OrderController {
    private OrderService orderService;
    private PaymentService paymentService;
    private EmailService emailService;
    
    public OrderController() {
        // 手动创建所有依赖对象
        this.orderService = new OrderService();
        this.paymentService = new PaymentService();
        this.emailService = new EmailService();
    }
    
    public void processOrder(Order order) {
        orderService.saveOrder(order);
        paymentService.processPayment(order);
        emailService.sendConfirmation(order);
    }
}

使用 Spring 框架:

!leading-relaxed 复制代码
@RestController
public class OrderController {
    @Autowired
    private OrderService orderService;
    @Autowired
    private PaymentService paymentService;
    @Autowired
    private EmailService emailService;
    
    @PostMapping("/orders")
    public void processOrder(@RequestBody Order order) {
        orderService.saveOrder(order);
        paymentService.processPayment(order);
        emailService.sendConfirmation(order);
    }
}

看起来差别不大?让我们继续往下看...

传统开发方式的痛点

1. 对象创建的噩梦

想象一下,当你的项目变得复杂时:

!leading-relaxed 复制代码
public class OrderService {
    private UserRepository userRepository;
    private ProductRepository productRepository;
    private InventoryService inventoryService;
    private DiscountService discountService;
    private AuditService auditService;
    
    public OrderService() {
        // 每个依赖都需要手动创建
        this.userRepository = new UserRepository();
        this.productRepository = new ProductRepository();
        this.inventoryService = new InventoryService();
        this.discountService = new DiscountService();
        this.auditService = new AuditService();
        
        // 但是等等...这些服务可能还有自己的依赖!
        // UserRepository可能需要DataSource
        // InventoryService可能需要CacheManager
        // 这会形成一个复杂的依赖链...
    }
}

问题显而易见:

  • 代码臃肿:构造函数被大量的对象创建代码污染
  • 硬编码依赖:如果要更换实现,必须修改源代码
  • 测试困难:无法轻易mock依赖对象进行单元测试
  • 维护噩梦:依赖关系复杂,牵一发而动全身

2. 配置地狱

还记得那些冗长的XML配置文件吗?

!leading-relaxed 复制代码
<!-- 传统的XML配置,数百行的重复配置 -->
<bean id="dataSource" class="com.mysql.jdbc.Driver">
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="root"/>
    <property name="password" value="password"/>
</bean>

<bean id="userRepository" class="com.example.UserRepository">
    <property name="dataSource" ref="dataSource"/>
</bean>

<bean id="orderService" class="com.example.OrderService">
    <property name="userRepository" ref="userRepository"/>
    <property name="productRepository" ref="productRepository"/>
    <!-- ...更多配置... -->
</bean>

3. 测试的痛苦

!leading-relaxed 复制代码
// 传统方式下的单元测试
public class OrderServiceTest {
    @Test
    public void testCreateOrder() {
        // 为了测试OrderService,需要创建所有的依赖
        UserRepository userRepo = new UserRepository();
        ProductRepository productRepo = new ProductRepository();
        InventoryService inventoryService = new InventoryService();
        // ...还有更多依赖
        
        OrderService orderService = new OrderService();
        // 测试代码比业务代码还复杂!
    }
}

Spring带来的革命性改变

现在,让我们看看Spring是如何优雅地解决这些问题的:

1. 告别手动创建对象

!leading-relaxed 复制代码
@Service
public class OrderService {
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private ProductRepository productRepository;
    @Autowired
    private InventoryService inventoryService;
    
    // 构造函数变得干净简洁
    // 所有依赖由Spring自动注入
    
    public void createOrder(Order order) {
        // 专注于业务逻辑,而不是对象创建
        User user = userRepository.findById(order.getUserId());
        // ...业务逻辑
    }
}

2. 配置变得简单

!leading-relaxed 复制代码
@Configuration
@ComponentScan("com.example")
public class AppConfig {
    // 一个注解胜过千行XML
}

// 或者更简单,使用Spring Boot
@SpringBootApplication
public class Application {
    // 零配置启动!
}

3. 测试变得轻松

!leading-relaxed 复制代码
@ExtendWith(SpringExtension.class)
class OrderServiceTest {
    @Mock
    private UserRepository userRepository;
    
    @InjectMocks
    private OrderService orderService;
    
    @Test
    void testCreateOrder() {
        // 轻松mock依赖,专注测试业务逻辑
        when(userRepository.findById(1L)).thenReturn(mockUser);
        
        orderService.createOrder(testOrder);
        
        verify(userRepository).findById(1L);
    }
}

真实场景模拟:一个电商系统的对比

让我们通过一个真实的电商系统场景来感受这种差异:

我们假设一个用户下单,系统需要:

  1. 扣减库存

  2. 执行支付

  3. 返回下单结果

传统方式的订单处理类:

!leading-relaxed 复制代码
// 传统方式:自己创建依赖对象
public class OrderService {
    private PaymentService paymentService;
    private InventoryService inventoryService;

    public OrderService() {
        this.paymentService = new PaymentService(); // 主动创建依赖对象
        this.inventoryService = new InventoryService();
    }

    public void createOrder() {
        inventoryService.deductStock();
        paymentService.pay();
        System.out.println("订单创建成功!");
    }

    public static void main(String[] args) {
        OrderService orderService = new OrderService();
        orderService.createOrder();
    }
}

class PaymentService {
    public void pay() {
        System.out.println("支付成功!");
    }
}

class InventoryService {
    public void deductStock() {
        System.out.println("库存扣减成功!");
    }
}

问题:

  • 代码耦合严重,OrderService 无法方便地更换或测试依赖组件

  • 不利于维护,不利于扩展,不易于单元测试。

Spring方式的订单处理类:

  1. 定义各个组件
!leading-relaxed 复制代码
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Service
public class OrderService {

    @Autowired
    private PaymentService paymentService;

    @Autowired
    private InventoryService inventoryService;

    public void createOrder() {
        inventoryService.deductStock();
        paymentService.pay();
        System.out.println("订单创建成功!");
    }
}

@Service
class PaymentService {
    public void pay() {
        System.out.println("支付成功!");
    }
}

@Service
class InventoryService {
    public void deductStock() {
        System.out.println("库存扣减成功!");
    }
}
  1. 启动类模拟运行
java 复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class ECommerceApp {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(ECommerceApp.class, args);
        OrderService orderService = context.getBean(OrderService.class);
        orderService.createOrder();
    }
}

你准备好了吗?

看到这里,你是否已经感受到了Spring框架的强大魅力?它不仅仅是一个工具,更是一种编程思想的革命。

Spring解决的不仅仅是技术问题,更是开发效率和代码质量的问题:

  • 让开发者专注业务逻辑,而不是基础设施
  • 让代码更加简洁、优雅、可维护
  • 让测试变得简单,提高代码质量
  • 让团队协作更加高效

那么问题来了:Spring是如何做到这一切的?

答案就在IoC(控制反转)和DI(依赖注入)这两个核心概念中。理解了它们,你就理解了Spring的灵魂,也就掌握了现代Java开发的精髓。

接下来,让我们深入探索这个让Java开发变得如此优雅的奥秘吧!

结尾:

如果你读到了这里,请不要因为戛然而止感到惊讶,因为笔者想要在下一篇博客中详细介绍 Ioc & DI

因此这是引子博客,笔者希望读者明白为什么需要IoC和DI,珍惜Spring带来的便利。

相关推荐
啾啾Fun1 分钟前
精粹汇总:大厂编程规范(持续更新)
后端·规范
软件开发技术深度爱好者1 分钟前
python类成员概要
开发语言·python
Dxy12393102165 分钟前
python如何将word的doc另存为docx
开发语言·python·word
yt948329 分钟前
lua读取请求体
后端·python·flask
CodeWithMe14 分钟前
【软件开发】什么是DSL
开发语言
IT_102414 分钟前
springboot从零入门之接口测试!
java·开发语言·spring boot·后端·spring·lua
我命由我1234536 分钟前
VSCode - VSCode 转换英文字母的大小写
开发语言·javascript·ide·vscode·编辑器·html·软件工具
湖北二师的咸鱼44 分钟前
c#和c++区别
java·c++·c#
weixin_418007601 小时前
软件工程的实践
java
皮皮林5511 小时前
项目终于用上了 Spring 状态机,太优雅了!
spring