@Inject 注释如何知道在同一接口下实例化哪个具体 class?
How @Inject annotation would know which concrete class to instantiate under same interface?
我在 Android 应用程序中使用 Dagger2.0
我对 @Inject 注释感到困惑。我有两个实现相同接口的具体 class。 我正在使用 @Inject 注释注入具体 class 之一。 这里,@Inject 注释如何决定要实例化哪个具体 class。
示例:
我只有一个接口。
Product.java
public interface Product {}
总共有两个具体的 classes ProductOne 和 ProductTwo。
ProductOne.class
public class ProductOne implements Product{
@Inject
public ProductOne() {}
}
打包class是客户端
Packaging.java
public class Packaging{
@Inject
public Packaging(Product product){}
}
到目前为止,我的包 class 使用 ProductOne class 的实例。
困惑:
如果我有另一个具体的 class ProductTwo 带有 @Inject 注释。
public class ProductTwo implements Product {
@Inject
public ProductTwo() {}
}
现在在我的包装中 class 我想使用 ProductTwo 的实例 class,那么这个 @Inject 注释此时可以工作吗?
这个例子将不起作用。对于这种情况,我们必须使用 @Named 注释。
对于上面的示例,在我们的 Dagger 打包模块中,我们必须提供 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
注释只是使用 javax.inject
中包含的 @Qualifier
注释
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
String value() default "";
}
我们不必提供此声明,因此 Dagger 会为我们做这件事。
我假设您没有提到任何模块或组件,您对它们以及它们如何协同工作不是很熟悉。
您的示例将无法运行,因为 Dagger 2 不知道要生成产品,它需要使用 ProductOne 或 ProductTwo 之一 类。尽管 Dagger 2 会处理它们(因为它们都被标记为@Inject),但它不会自动假定仅仅因为它们实现了 Product 就应该在那里使用它们。原因是在这种情况下,当有多个时,它不知道该使用哪个。
因此,您必须使用 Product 接口从 ProductOne 或 ProductTwo 创建绑定。你通过一个模块来做到这一点。
@Module
public class ProductOneModule {
@Provides Product provideProduct(ProductOne productOne) {
return productOne;
}
}
一个模块只是提供一组可重用的绑定。除非它们被组件使用,否则它们不会被实际使用(或验证)。组件是封装所有这些信息并管理它们的创建的东西,使用为 类s 和 @Inject 构造函数创建的模块及其绑定和工厂。
如果您创建这样的组件,那么 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
的两个不同实现一起使用,例如您的生产和测试。
当然,您可以使用两种不同的实现,即使它是用限定符注释的,但您仍然必须使用多种这种技术。
我在 Android 应用程序中使用 Dagger2.0
我对 @Inject 注释感到困惑。我有两个实现相同接口的具体 class。 我正在使用 @Inject 注释注入具体 class 之一。 这里,@Inject 注释如何决定要实例化哪个具体 class。
示例:
我只有一个接口。
Product.java
public interface Product {}
总共有两个具体的 classes ProductOne 和 ProductTwo。
ProductOne.class
public class ProductOne implements Product{
@Inject
public ProductOne() {}
}
打包class是客户端
Packaging.java
public class Packaging{
@Inject
public Packaging(Product product){}
}
到目前为止,我的包 class 使用 ProductOne class 的实例。
困惑:
如果我有另一个具体的 class ProductTwo 带有 @Inject 注释。
public class ProductTwo implements Product {
@Inject
public ProductTwo() {}
}
现在在我的包装中 class 我想使用 ProductTwo 的实例 class,那么这个 @Inject 注释此时可以工作吗?
这个例子将不起作用。对于这种情况,我们必须使用 @Named 注释。
对于上面的示例,在我们的 Dagger 打包模块中,我们必须提供 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
注释只是使用 javax.inject
@Qualifier
注释
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
String value() default "";
}
我们不必提供此声明,因此 Dagger 会为我们做这件事。
我假设您没有提到任何模块或组件,您对它们以及它们如何协同工作不是很熟悉。
您的示例将无法运行,因为 Dagger 2 不知道要生成产品,它需要使用 ProductOne 或 ProductTwo 之一 类。尽管 Dagger 2 会处理它们(因为它们都被标记为@Inject),但它不会自动假定仅仅因为它们实现了 Product 就应该在那里使用它们。原因是在这种情况下,当有多个时,它不知道该使用哪个。
因此,您必须使用 Product 接口从 ProductOne 或 ProductTwo 创建绑定。你通过一个模块来做到这一点。
@Module
public class ProductOneModule {
@Provides Product provideProduct(ProductOne productOne) {
return productOne;
}
}
一个模块只是提供一组可重用的绑定。除非它们被组件使用,否则它们不会被实际使用(或验证)。组件是封装所有这些信息并管理它们的创建的东西,使用为 类s 和 @Inject 构造函数创建的模块及其绑定和工厂。
如果您创建这样的组件,那么 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
的两个不同实现一起使用,例如您的生产和测试。
当然,您可以使用两种不同的实现,即使它是用限定符注释的,但您仍然必须使用多种这种技术。