Dagger 2简介

2025/04/25

1. 简介

在本教程中,我们将介绍Dagger 2-一个快速、轻量级的依赖注入框架。

该框架适用于Java和Android,但编译时注入带来的高性能使其成为后者的领先解决方案。

2. 依赖注入

提醒一下,依赖注入是更通用的控制反转原则的具体应用,其中程序流程由程序本身控制。

它是通过外部组件实现的,该组件提供其他对象所需的对象(或依赖)的实例。

不同的框架实现依赖注入的方式也不同。其中最显著的区别之一就是注入是在运行时还是编译时进行。

运行时DI通常基于反射,它使用起来更简单,但运行时速度较慢,Spring就是一个运行时DI框架的示例。

另一方面,编译时DI基于代码生成,这意味着所有重量级操作都在编译期间执行,编译时DI增加了复杂性,但通常执行速度更快。

Dagger 2就属于这一类。

3. Maven/Gradle配置

为了在项目中使用Dagger,我们需要将dagger依赖添加到pom.xml中:

<dependency>
    <groupId>com.google.dagger</groupId>
    <artifactId>dagger</artifactId>
    <version>2.16</version>
</dependency>

此外,我们还需要包含用于将带注解的类转换为用于注入的代码的Dagger编译器

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.11.0</version>
    <configuration>
         <annotationProcessorPaths>
              <path>
                  <groupId>com.google.dagger</groupId>
                  <artifactId>dagger-compiler</artifactId>
                  <version>2.16</version>
              </path>
         </annotationProcessorPaths>
    </configuration>
</plugin>

通过此配置,Maven将把生成的代码输出到target/generated-sources/annotations中。

因此,如果我们想使用IDE的代码补全功能,可能需要进一步配置它。有些IDE直接支持注解处理器,而有些IDE则可能需要我们将此目录添加到构建路径中。

或者,如果我们使用带有Gradle的Android,我们可以同时包含这两个依赖:

compile 'com.google.dagger:dagger:2.16'
annotationProcessor 'com.google.dagger:dagger-compiler:2.16'

现在我们的项目中有了Dagger,让我们创建一个示例应用程序来查看它是如何工作的。

4. 实现

为了举例,我们将尝试通过注入零部件来制造一辆汽车。

现在,Dagger在很多地方使用标准JSR-330注解,其中之一就是@Inject。

我们可以将注解添加到字段或构造函数中,但是,由于Dagger不支持私有字段的注入,因此我们将使用构造函数注入来保持封装性:

public class Car {

    private Engine engine;
    private Brand brand;

    @Inject
    public Car(Engine engine, Brand brand) {
        this.engine = engine;
        this.brand = brand;
    }

    // getters and setters
}

接下来,我们将实现执行注入的代码,更具体地说,我们将创建:

  • 模块,它是一个提供或构建对象依赖的类
  • 组件,用于生成注入器的接口

复杂的项目可能包含多个模块和组件,但由于我们处理的是一个非常基本的程序,因此每个模块和组件一个就足够了。

让我们看看如何实现它们。

4.1 模块

要创建模块,我们需要使用@Module注解来标注该类,此注解表明该类可以将依赖提供给容器:

@Module
public class VehiclesModule {
}

然后,我们需要在构建依赖的方法上添加@Provides注解

@Module
public class VehiclesModule {
    @Provides
    public Engine provideEngine() {
        return new Engine();
    }

    @Provides
    @Singleton
    public Brand provideBrand() { 
        return new Brand("Tuyucheng"); 
    }
}

另外,请注意,我们可以配置给定依赖的作用域。在本例中,我们为Brand实例赋予了单例作用域,以便所有汽车实例共享同一个品牌对象。

4.2 组件

接下来,我们将创建组件接口,该类将生成Car实例,并注入由VehiclesModule提供的依赖。

简而言之,我们需要一个返回Car的方法签名,并且需要用@Component注解标记该类:

@Singleton
@Component(modules = VehiclesModule.class)
public interface VehiclesComponent {
    Car buildCar();
}

注意我们如何将模块类作为参数传递给@Component注解,如果不这样做,Dagger就不知道如何构建汽车的依赖

此外,由于我们的模块提供了一个单例对象,我们必须为我们的组件提供相同的范围,因为Dagger不允许无范围的组件引用有范围的绑定

4.3 客户端代码

最后,我们可以运行mvn compile来触发注解处理器并生成注入器代码。

之后,我们会发现我们的组件实现与接口同名,只是前缀为“Dagger”:

@Test
public void givenGeneratedComponent_whenBuildingCar_thenDependenciesInjected() {
    VehiclesComponent component = DaggerVehiclesComponent.create();

    Car carOne = component.buildCar();
    Car carTwo = component.buildCar();

    Assert.assertNotNull(carOne);
    Assert.assertNotNull(carTwo);
    Assert.assertNotNull(carOne.getEngine());
    Assert.assertNotNull(carTwo.getEngine());
    Assert.assertNotNull(carOne.getBrand());
    Assert.assertNotNull(carTwo.getBrand());
    Assert.assertNotEquals(carOne.getEngine(), carTwo.getEngine());
    Assert.assertEquals(carOne.getBrand(), carTwo.getBrand());
}

5. Spring类比

熟悉Spring的人可能已经注意到这两个框架之间的一些相似之处。

Dagger的@Module注解使容器能够感知类,其方式与Spring的其他构造型注解(例如@Service、@Controller等)非常相似。同样,@Provides和@Component几乎分别相当于Spring的@Bean和@Lookup。

Spring也有其自己的@Scope注解,与@Singleton相关,但请注意这里已经存在的另一个区别是Spring默认采用单例范围,而Dagger默认采用Spring开发人员可能称为原型范围的范围,每次需要依赖时都会调用提供程序方法。

6. 总结

在本文中,我们通过一个基本示例介绍了如何设置和使用Dagger 2,我们还探讨了运行时注入和编译时注入之间的区别。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章