匕首:访问在子组件中创建的两个级别的项目
Dagger: Accessing item created two levels deep in a subcomponent
我有一个子组件需要从中提取一些东西:
@Subcomponent(modules = {SubModule.class})
@SubScope
public interface SubComp {
// ...
Thing getThing();
}
每次调用 #getThing
时,我都需要一个 Thing
的新实例。
Thing
也有自己的项目轨道,需要用它来创建。自然地,我的直觉是为它创建另一个子组件:
@Subcomponent(modules = {ModuleThing.class})
@ThingScope
public interface SubCompThing {
// ...
}
但这是我的难题:实际上应该创建哪段代码 Thing
?
如果我将提供程序放入 SubModule
,然后我需要将该实例绑定到 SubCompThing
,但我收到有关绑定多个实例的警告。该警告在我的构建中是致命的,实际上声明该警告将来会成为错误:
@Module(subcomponents = {SubCompThing.class})
interface SubModule {
@Provides
static providesThing(SubCompThing.Factory thingCompFactory) {
Thing thing = new Thing();
thingComp = thingCompFactory.create(thing); // Warning about duplicate binding.
// Do some stuff with thingComp
return thing;
}
}
如果我 SubCompThing
自己直接创建 Thing
,我的警告会变成一个错误,问题相同:
@Module
interface ModuleThing {
@Provides
@ThingScope
static Thing providesThing() {
return new Thing();
}
}
@Module(subcomponents = {SubCompThing.class})
interface SubModule {
@Provides
static Thing providesThing(SubCompThing.Factory thingCompFactory) {
thingComp = thingCompFactory.create();
// Do some stuff with thingComp
return thingComp.getThing();
}
}
(Dagger 编译 Thing
绑定了两次,因为它有两个提供程序。)
我怎样才能让我的顶级 SubComp
return 新的 Thing
随需应变,并且让每个 Thing
都有自己的子组件实例与他们?
您将需要使用 qualifier annotation。
这里问题的根源在于子组件从它们的父组件继承绑定,因此在您最深的子组件 SubCompThing 中,您组件上暴露的 Thing 绑定很可能会注入您在 SubComp 的 SubModule 中安装的 Thing 绑定...因为 Dagger 不知道您在 SubModule 中的 @Provides 方法本身将调用 SubCompThing 的 getThing()
方法!
正如我们在评论中讨论的那样,您所描述的内容非常类似于 Dagger 关于 subcomponents for encapsulation 的官方文档,它偷偷地描述但没有描述它对限定符注释的依赖:
@Subcomponent(modules = DatabaseImplModule.class)
interface DatabaseComponent {
@PrivateToDatabase Database database();
/* ^^^^^^^^^^^^ */
}
这是消除歧义的技巧:您的 ModuleThing 应该绑定 @Provides static @PrivateThing Thing
,而您的 SubCompThing 应该公开 @PrivateThing Thing getThing()
,这样唯一绑定的不合格事物就绑定在您的 SubModule 中。 (顺便说一句,这将允许您在 SubCompThing 中注入一个事物,委托 SubModule 的实现从 ModuleThing 的调用堆栈中创建一个全新的 SubCompThing 实例。)
我在这里定义了一个便宜的限定符注释,但欢迎您使用 @Named 来制作它的原型。
@Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface PrivateThing {}
@Module
interface ModuleThing {
@Provides
@ThingScope
@PrivateThing
static Thing providesThing() {
return new Thing();
}
}
@Subcomponent(modules = {ModuleThing.class})
@ThingScope
public interface SubCompThing {
@PrivateThing getThing();
}
到那时,您的 SubModule 将无需额外修改即可工作,将 Thing 的创建抽象为 SubCompThing 的黑盒实现(我更喜欢这种方式,这样您就不必带来 Thing 的实例化细节进入子模块)。这也意味着您可以保持 Thing 无作用域,这样您就可以实例化多个对象:当您调用提供者时,您会看到一些新的东西。 That way you can make both thing1
and thing2
.
我有一个子组件需要从中提取一些东西:
@Subcomponent(modules = {SubModule.class})
@SubScope
public interface SubComp {
// ...
Thing getThing();
}
每次调用 #getThing
时,我都需要一个 Thing
的新实例。
Thing
也有自己的项目轨道,需要用它来创建。自然地,我的直觉是为它创建另一个子组件:
@Subcomponent(modules = {ModuleThing.class})
@ThingScope
public interface SubCompThing {
// ...
}
但这是我的难题:实际上应该创建哪段代码 Thing
?
如果我将提供程序放入 SubModule
,然后我需要将该实例绑定到 SubCompThing
,但我收到有关绑定多个实例的警告。该警告在我的构建中是致命的,实际上声明该警告将来会成为错误:
@Module(subcomponents = {SubCompThing.class})
interface SubModule {
@Provides
static providesThing(SubCompThing.Factory thingCompFactory) {
Thing thing = new Thing();
thingComp = thingCompFactory.create(thing); // Warning about duplicate binding.
// Do some stuff with thingComp
return thing;
}
}
如果我 SubCompThing
自己直接创建 Thing
,我的警告会变成一个错误,问题相同:
@Module
interface ModuleThing {
@Provides
@ThingScope
static Thing providesThing() {
return new Thing();
}
}
@Module(subcomponents = {SubCompThing.class})
interface SubModule {
@Provides
static Thing providesThing(SubCompThing.Factory thingCompFactory) {
thingComp = thingCompFactory.create();
// Do some stuff with thingComp
return thingComp.getThing();
}
}
(Dagger 编译 Thing
绑定了两次,因为它有两个提供程序。)
我怎样才能让我的顶级 SubComp
return 新的 Thing
随需应变,并且让每个 Thing
都有自己的子组件实例与他们?
您将需要使用 qualifier annotation。
这里问题的根源在于子组件从它们的父组件继承绑定,因此在您最深的子组件 SubCompThing 中,您组件上暴露的 Thing 绑定很可能会注入您在 SubComp 的 SubModule 中安装的 Thing 绑定...因为 Dagger 不知道您在 SubModule 中的 @Provides 方法本身将调用 SubCompThing 的 getThing()
方法!
正如我们在评论中讨论的那样,您所描述的内容非常类似于 Dagger 关于 subcomponents for encapsulation 的官方文档,它偷偷地描述但没有描述它对限定符注释的依赖:
@Subcomponent(modules = DatabaseImplModule.class)
interface DatabaseComponent {
@PrivateToDatabase Database database();
/* ^^^^^^^^^^^^ */
}
这是消除歧义的技巧:您的 ModuleThing 应该绑定 @Provides static @PrivateThing Thing
,而您的 SubCompThing 应该公开 @PrivateThing Thing getThing()
,这样唯一绑定的不合格事物就绑定在您的 SubModule 中。 (顺便说一句,这将允许您在 SubCompThing 中注入一个事物,委托 SubModule 的实现从 ModuleThing 的调用堆栈中创建一个全新的 SubCompThing 实例。)
我在这里定义了一个便宜的限定符注释,但欢迎您使用 @Named 来制作它的原型。
@Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface PrivateThing {}
@Module
interface ModuleThing {
@Provides
@ThingScope
@PrivateThing
static Thing providesThing() {
return new Thing();
}
}
@Subcomponent(modules = {ModuleThing.class})
@ThingScope
public interface SubCompThing {
@PrivateThing getThing();
}
到那时,您的 SubModule 将无需额外修改即可工作,将 Thing 的创建抽象为 SubCompThing 的黑盒实现(我更喜欢这种方式,这样您就不必带来 Thing 的实例化细节进入子模块)。这也意味着您可以保持 Thing 无作用域,这样您就可以实例化多个对象:当您调用提供者时,您会看到一些新的东西。 That way you can make both thing1
and thing2
.