@Inject注释如何知道在同一个接口下实例化哪个具体类?

问题描述 投票:5回答:2

我在Android应用程序中使用Dagger2.0。

我对@Inject注释很困惑。我有两个实现相同接口的具体类。我正在使用@Inject注释注入一个具体类。这里,@ Inject注释如何决定实例化哪个具体类。

例:

我有一个界面。

product.Java

public interface Product {}

ProductOne和ProductTwo共有两个具体的类。

ProductOne.class

public class ProductOne implements Product{

@Inject
public ProductOne() {}

}

包装类是客户端。

packaging.Java

public class Packaging{

@Inject
public Packaging(Product product){}

}

直到这一刻,我的包类使用了ProductOne类的实例。

混乱:

如果我有另一个具有@Inject批注的具体类ProductTwo。

public class ProductTwo implements Product {

@Inject
public ProductTwo() {}

}

现在在我的Packaging类中我想使用ProductTwo类的实例,那么这个@Inject注释会在这个时候起作用吗?

java android dependency-injection dagger-2
2个回答
3
投票

我假设你没有提到任何你不熟悉它们的模块或组件以及它们如何协同工作。

您的示例将不起作用,因为Dagger 2不知道为了生成产品,它需要使用ProductOne或ProductTwo类之一。即使Dagger 2会处理它们(因为它们都标有@Inject),它也不会自动假设只是因为它们实现了Product应该在那里使用它们。这样做的原因是,当这种情况不止一个时,它不会知道使用哪一个。

因此,您必须使用Product Product接口从ProductOne或ProductTwo创建绑定。你通过一个模块来做到这一点。

@Module
public class ProductOneModule {
  @Provides Product provideProduct(ProductOne productOne) {
    return productOne;
  }
}

模块只提供一组可重用的绑定。除非组件使用它们,否则它们实际上不会被使用(或验证)。组件是封装所有这些信息并管理它们的创建的东西,使用模块及其绑定和为@sject构造函数创建classess的工厂。

如果您创建这样的组件,那么dagger 2将会失败,因为如上所述,它不知道如何生成产品。

@Component
public interface PackagerOneComponent {
  Packager packager();
}

错误将是这样的:

Product cannot be provided without an @Provides-annotated method.
    Packager.(Product product)
    [parameter: Product product]

这意味着当试图创建一个Packager对象时,它无法为其Product参数找到合适的绑定。解决这个问题的方法是使用Product < ProductOne的绑定来指定模块。

@Component(modules = ProductOneModule.class)
public interface PackagerOneComponent {
  Packager packager();
}

现在它知道要创建一个Product它需要调用ProductOneModule.provideProduct(ProductOne)并且为了调用它需要创建一个ProductOne它知道该怎么做因为你用@Inject标记了它的一个构造函数。

当然,如果您想使用ProductTwo,那么您可以创建另一个模块和组件。

@Module
public class ProductTwoModule {
  @Provides Product provideProduct(ProductTwo productTwo) {
    return productTwo;
  }
}

@Component(modules = ProductTwoModule.class)
public interface PackagerTwoComponent {
  Packager packager();
}

在这种情况下使用限定符(自定义或限定)的问题是使用限定符将注入点与特定实现紧密耦合。在某些情况下,这肯定是必需的,例如如果你有两个Long实例,其中一个是超时,一个是端口,你不希望它们混淆,因此你肯定需要使用限定符来区分它们。

但是,在这种情况下,某些用户或包装可能希望使用ProductOne,而有些用户则希望使用ProductTwo。否则,Packager应该直接使用ProductOne或ProductTwo并避开接口。

这种方法允许您的代码的两个不同部分使用Packager和两个不同的Product实现,例如你的生产和测试。

当然,即使使用限定符进行注释,您也可以使用两种不同的实现,但是您仍然需要使用各种这种技术。


4
投票

这个例子不起作用。我们必须在这种情况下使用@Named注释。

对于我们的Dagger Packaging模块中的上述示例,我们必须提供ProductOneProductTwo依赖项。

@Provides @Named("product one") Product provideProductOne() {
    return new ProductOne();
}


@Provides @Named("product two") Product provideProductTwo() {
    return new ProductTwo();
} 

现在,当我们需要注入此依赖项时,我们可以按照以下方式注入它。

public class Packaging{

Product product;

@Inject
public Packaging(@Named("product one") Product product){
    this.product = product;
}

}

如果我们需要ProductTwo的实例那么。

public class Packaging{

Product product;
@Inject
public Packaging(@Named("product two")Product product){
    this.product = product;
}

}

这个@Named注释只是使用@Qualifier中包含的javax.inject注释

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
  String value() default "";
}

我们不必提供此声明,因此Dagger为我们这样做。

© www.soinside.com 2019 - 2024. All rights reserved.