依赖注入的本质是一种设计模式,最终目的还是为了实现松耦合(并非完全不需要类之间的耦合关系,而是降低耦合)

传统的耦合问题

class Car {
    private Engine engine = new Engine(); // Car 类负责创建 Engine 实例

    public void start() {
        engine.start();
        System.out.println("Car is running.");
    }
}

class Engine {
    public void start() {
        System.out.println("Engine started.");
    }
}

从上面的代码我们可以看到,如果Car要调用Engine的功能,就需要在内部创建一个Engine的实例来对其进行调用,如果我们要更换Engine的实现,就需要连带着Car这里的代码也一起修改。

依赖注入的解决方案

class Car {
    private Engine engine;

    public Car(Engine engine) { // 通过构造器注入 Engine
        this.engine = engine;
    }

    public void start() {
        engine.start();
        System.out.println("Car is running.");
    }
}

interface Engine { // 定义 Engine 接口
    void start();
}

class GasEngine implements Engine {
    @Override
    public void start() {
        System.out.println("Gas Engine started.");
    }
}

class ElectricEngine implements Engine {
    @Override
    public void start() {
        System.out.println("Electric Engine started.");
    }
}

Engine对象的创建并不由Car负责,同时Engine对象可以有多种实现,至于具体传进去的则是由Car之外的构造器来考虑。

同时,在Spring容器中,Car与Engine是这样配置的:

<bean id="gasEngine" class="GasEngine"/>
<bean id="electricEngine" class="ElectricEngine"/>
<bean id="car" class="Car">
    <constructor-arg ref="gasEngine"/>
</bean>

也就是说如果我们要将油动改为电动,只需要更改ref中引用的目标就好了.

在Spring中,依赖注入是通过注解的方式来实现的。

以下是四种与依赖注入有关的注解(generated by Google Gemini 2.0)

1. @Component

  • 通用组件注解: @Component 是一个通用的注解,用于标识一个类为 Spring 容器管理的组件(Bean)。它可以应用于任何类,表示这个类会被 Spring 容器自动扫描并注册为一个 Bean。

  • 基本功能: 相当于 XML 配置中的 <bean> 标签,负责将类实例化并放入 IoC 容器中管理其生命周期。

  • 示例:

    @Component
    public class MyComponent {
        // ...
    }

2. @Service

  • 服务层组件注解: @Service@Component 的一个特化版本,用于标识服务层(Service Layer)的组件。

  • 语义化: 它在语义上更清晰地表明了该类扮演的角色是业务逻辑的处理者。

  • 无额外功能(通常): 在 Spring 框架的核心功能上,@Service@Component 并没有本质区别,它们都将类注册为 Bean。但在某些情况下,Spring 或第三方库可能会基于 @Service 添加一些特定的功能,例如事务管理等。

  • 最佳实践: 建议使用 @Service 标识服务层组件,提高代码的可读性和可维护性。

  • 示例:

    @Service
    public class MyService {
        // ...
    }

3. @Repository

  • 持久层组件注解: @Repository 也是 @Component 的一个特化版本,用于标识数据访问层(Data Access Layer/Persistence Layer)的组件,例如 DAO(Data Access Object)。

  • 异常转换: @Repository 最重要的作用是提供异常转换功能。当数据访问过程中发生异常时,@Repository 注解会将底层的特定异常(例如 JDBC 异常、JPA 异常)转换为 Spring 的 DataAccessException 及其子类,从而提供一致的异常处理机制,并隐藏底层的技术细节。

  • 示例:

    @Repository
    public class MyRepository {
        // ...
    }

4. @Autowired

  • 依赖注入注解: @Autowired 用于自动装配(注入)Bean。它可以用于构造器、Setter 方法、字段上。

  • 自动查找: Spring 容器会根据类型(Type)自动查找匹配的 Bean,并将其注入到目标对象中。如果存在多个匹配的 Bean,可以使用 @Qualifier 注解指定 Bean 的名称。

  • 简化配置: @Autowired 大大简化了依赖注入的配置,减少了 XML 配置或 Java 配置的代码量。

  • 示例:

    @Service
    public class MyService {
        @Autowired // 自动注入 MyRepository
        private MyRepository myRepository;
    
        // ...
    }

总结:

注解

作用

适用场景

关键特性/区别

@Component

标识一个类为 Spring 容器管理的 Bean

任何类

通用组件注解

@Service

标识服务层组件

服务层(Service Layer)

语义化更清晰,通常无额外功能,最佳实践推荐使用

@Repository

标识数据访问层组件,提供异常转换

数据访问层(Data Access Layer)

提供异常转换功能,将底层异常转换为 Spring 异常

@Autowired

自动装配 Bean,进行依赖注入

用于构造器、Setter 方法、字段

根据类型自动查找 Bean,简化配置

导出到 Google 表格

联系:

@Service@Repository 本质上都是 @Component 的特例,它们都使用 @Component 的功能将类注册为 Bean。而 @Autowired 则用于将这些 Bean 注入到其他 Bean 中,实现依赖注入。

示例代码整合:

@Repository
public class MyRepository {
    public void saveData() {
        // ...
    }
}

@Service
public class MyService {
    @Autowired
    private MyRepository myRepository;

    public void doSomething() {
        myRepository.saveData();
    }
}

@Component
public class MyComponent {
    @Autowired
    private MyService myService;

    public void execute() {
        myService.doSomething();
    }
}

在这个例子中,MyRepositoryMyServiceMyComponent 都被 Spring 容器管理,并且通过 @Autowired 实现了依赖注入。

一个还在寻找自己的三流开发者