我在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注释会在这个时候起作用吗?
答案 0 :(得分:4)
此示例无效。对于这种情况,我们必须使用@Named注释。
对于我们的Dagger Packaging模块中的上述示例,我们必须提供ProductOne
和ProductTwo
依赖项。
@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为我们这样做。
答案 1 :(得分: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
,它知道该怎么做,因为你&#39 ;使用@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
的两种不同实现,例如你的生产和测试。
当然,即使使用限定符注释,您也可以使用两种不同的实现,但是您仍然需要使用各种技术。